

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    
    <title>A Tutorial for the Odespy Interface to ODE Solvers</title>
    
    <link rel="stylesheet" href="_static/default.css" type="text/css" />
    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
    
    <script type="text/javascript">
      var DOCUMENTATION_OPTIONS = {
        URL_ROOT:    './',
        VERSION:     '1.0',
        COLLAPSE_INDEX: false,
        FILE_SUFFIX: '.html',
        HAS_SOURCE:  true
      };
    </script>
    <script type="text/javascript" src="_static/jquery.js"></script>
    <script type="text/javascript" src="_static/underscore.js"></script>
    <script type="text/javascript" src="_static/doctools.js"></script>
    <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
    <script type="text/javascript" src="_static/sidebar.js"></script>

        <script src="http://sagecell.sagemath.org/static/jquery.min.js"></script>
        <script src="http://sagecell.sagemath.org/static/embedded_sagecell.js"></script>

        <script>sagecell.makeSagecell({inputLocation: ".sage"});</script>

        <style type="text/css">
                .sagecell .CodeMirror-scroll {
                        overflow-y: hidden;
                        overflow-x: auto;
                }
                .sagecell .CodeMirror {
                        height: auto;
                }
        </style>

    
    <link rel="top" title="A Tutorial for the Odespy Interface to ODE Solvers" href="index.html" />
    <link rel="prev" title="A Tutorial for the Odespy Interface to ODE Solvers" href="index.html" />
 
  
       <style type="text/css">
         div.admonition {
           background-color: whiteSmoke;
           border: 1px solid #bababa;
         }
       </style>
      </head>
    
  <body>
    <div class="related">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             accesskey="I">index</a></li>
        <li class="right" >
          <a href="index.html" title="A Tutorial for the Odespy Interface to ODE Solvers"
             accesskey="P">previous</a> |</li>
        <li><a href="index.html">A Tutorial for the Odespy Interface to ODE Solvers</a> &raquo;</li> 
      </ul>
    </div>  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body">
            
  <div class="section" id="a-tutorial-for-the-odespy-interface-to-ode-solvers">
<h1>A Tutorial for the Odespy Interface to ODE Solvers<a class="headerlink" href="#a-tutorial-for-the-odespy-interface-to-ode-solvers" title="Permalink to this headline">¶</a></h1>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field-odd field"><th class="field-name">Authors:</th><td class="field-body">Hans Petter Langtangen, Liwei Wang</td>
</tr>
<tr class="field-even field"><th class="field-name">Date:</th><td class="field-body">May 1, 2015</td>
</tr>
</tbody>
</table>
<p>The Odespy package makes it easy to specify an ODE problem in
Python and get it solved by a wide variety of different numerical
methods and software.</p>
<span class="target" id="ode-sec-motivation"></span><div class="section" id="motivation">
<h2>Motivation<a class="headerlink" href="#motivation" title="Permalink to this headline">¶</a></h2>
<p>The Odespy package grew out of the desire to have a unified interface
to lots of different methods and software for ODEs. Consider the
ODE problem</p>
<div class="math">
\[y'' = 3 (1 - y^2) y' - y,\quad y(0)=2,\ y'(0)=1,\]</div>
<p>known as the van der Pool oscillator. The solution is
desired at 150 equally spaced time levels in
the interval <tt class="docutils literal"><span class="pre">[0,</span> <span class="pre">30]</span></tt>.</p>
<div class="section" id="traditional-approach">
<h3>Traditional Approach<a class="headerlink" href="#traditional-approach" title="Permalink to this headline">¶</a></h3>
<p>We want to solve this problem
by three well-known routines:</p>
<blockquote>
<div><ol class="arabic simple">
<li><tt class="docutils literal"><span class="pre">LSODE</span></tt> from ODEPACK (adaptive Adams and BDF methods)</li>
<li><tt class="docutils literal"><span class="pre">ode45</span></tt> from MATLAB (adaptive Runge-Kutta 4-5-th order)</li>
<li><tt class="docutils literal"><span class="pre">vode</span></tt> from Python (adaptive Adams and BDF methods)</li>
</ol>
</div></blockquote>
<p>All of these routines require the ODE problem to be on the form
<span class="math">\(u'=f(u,t)\)</span>, which means that
the second-order differential equation must be
recast as a system of two ODEs,</p>
<div class="math">
\[\frac{d}{dt}u^{(0)} = u^{(1)},\quad \frac{d}{dt}u^{(1)} =3(1-{(u^{(0)})}^2) u^{(1)} - u^{(0)},\]</div>
<p>and we have to identify the two components
of the <span class="math">\(f(u,t)\)</span> function:</p>
<div class="math">
\[f^{(0)}(u^{(0)}, u^{(1)}, t) = u^{(1)}, \quad
  f^{(1)}(u^{(0)}, u^{(1)}, t) =3(1-{(u^{(0)})}^2)u^{(1)} - u^{(0)}\]</div>
<p>The corresponding boundary conditions become</p>
<div class="math">
\[u^{(0)}(0)=2,\quad u^{(1)}(0)=1.\]</div>
<p>The mentioned ODE software needs a specification of the <span class="math">\(f(u,t)\)</span> formulas
through some user-written function that takes <span class="math">\(u\)</span> and <span class="math">\(t\)</span> as input and
delivers the vector <span class="math">\(f\)</span> as output.</p>
<div class="section" id="lsode">
<h4>LSODE<a class="headerlink" href="#lsode" title="Permalink to this headline">¶</a></h4>
<p>Application of <tt class="docutils literal"><span class="pre">LSODE</span></tt> and other ODEPACK routines requires the
ODE problem to be specified in FORTRAN and the solver to be called
from FORTRAN:</p>
<div class="highlight-fortran"><div class="highlight"><pre>     <span class="k">PROGRAM </span><span class="n">MAIN</span>
     <span class="k">EXTERNAL </span><span class="n">F</span>
     <span class="kt">INTEGER </span><span class="n">I</span><span class="p">,</span> <span class="n">IOPT</span><span class="p">,</span> <span class="n">IOUT</span><span class="p">,</span> <span class="n">ISTATE</span><span class="p">,</span> <span class="n">ITASK</span><span class="p">,</span> <span class="n">ITOL</span><span class="p">,</span> <span class="n">IWORK</span><span class="p">,</span>
    <span class="mi">1</span>   <span class="n">LRW</span><span class="p">,</span> <span class="n">LIW</span><span class="p">,</span> <span class="n">MF</span><span class="p">,</span> <span class="n">NEQ</span><span class="p">,</span> <span class="n">NOUT</span>
     <span class="kt">DOUBLE PRECISION </span><span class="n">ATOL</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">TOUT</span><span class="p">,</span> <span class="n">RTOL</span><span class="p">,</span> <span class="n">RWORK</span><span class="p">,</span> <span class="n">U</span><span class="p">,</span> <span class="n">URR</span>
     <span class="k">DIMENSION </span><span class="n">U</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="n">RWORK</span><span class="p">(</span><span class="mi">52</span><span class="p">),</span> <span class="n">IWORK</span><span class="p">(</span><span class="mi">20</span><span class="p">),</span> <span class="n">U1</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> <span class="n">U2</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
     <span class="n">NEQ</span> <span class="o">=</span> <span class="mi">2</span>
<span class="n">C</span>    <span class="n">SET</span> <span class="n">ADAMS</span> <span class="n">METHOD</span><span class="p">:</span>
     <span class="n">MF</span> <span class="o">=</span> <span class="mi">10</span>
<span class="n">C</span>    <span class="n">LET</span> <span class="n">TOLERANCES</span> <span class="n">BE</span> <span class="n">SCALARS</span> <span class="p">(</span><span class="nb">NOT </span><span class="n">ARRAYS</span><span class="p">):</span>
     <span class="n">ITOL</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">C</span>    <span class="k">USE </span><span class="n">ONLY</span> <span class="n">ABSOLUTE</span> <span class="n">TOLERANCE</span><span class="p">:</span>
     <span class="n">RTOL</span> <span class="o">=</span> <span class="mf">0.0</span><span class="n">D0</span>
     <span class="n">ATOL</span> <span class="o">=</span> <span class="mf">1.0</span><span class="n">D</span><span class="o">-</span><span class="mi">6</span>
     <span class="n">LRW</span> <span class="o">=</span> <span class="mi">52</span>
     <span class="n">LIW</span> <span class="o">=</span> <span class="mi">20</span>
<span class="n">C</span>    <span class="n">NUMBER</span> <span class="n">OF</span> <span class="nb">TIME </span><span class="n">STEPS</span><span class="p">:</span>
     <span class="n">NOUT</span> <span class="o">=</span> <span class="mi">150</span>
<span class="n">C</span>    <span class="k">FINAL </span><span class="nb">TIME</span><span class="p">:</span>
     <span class="n">TOUT</span> <span class="o">=</span> <span class="mi">3</span><span class="mf">0.0</span><span class="n">D0</span>
<span class="n">C</span>    <span class="n">INITIAL</span> <span class="n">CONDITIONS</span>
     <span class="n">T</span> <span class="o">=</span> <span class="mf">0.0</span><span class="n">D0</span>
     <span class="n">U</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">=</span> <span class="mf">2.0</span><span class="n">D0</span>
     <span class="n">U</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">=</span> <span class="mf">0.0</span><span class="n">D0</span>
     <span class="n">ITASK</span> <span class="o">=</span> <span class="mi">1</span>
     <span class="n">ISTATE</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">C</span>    <span class="k">CALL </span><span class="n">ADAPTIVE</span> <span class="nb">TIME </span><span class="n">STEPPING</span> <span class="n">AT</span> <span class="n">EACH</span> <span class="n">OF</span> <span class="n">THE</span> <span class="k">TARGET </span><span class="nb">TIME </span><span class="n">LEVELS</span>
     <span class="k">DO </span><span class="mi">100</span> <span class="n">IOUT</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">NOUT</span>
       <span class="k">CALL </span><span class="n">DLSODE</span><span class="p">(</span><span class="n">F</span><span class="p">,</span><span class="n">NEQ</span><span class="p">,</span><span class="n">U</span><span class="p">,</span><span class="n">T</span><span class="p">,</span><span class="n">TOUT</span><span class="p">,</span><span class="n">ITOL</span><span class="p">,</span><span class="n">RTOL</span><span class="p">,</span><span class="n">ATOL</span><span class="p">,</span><span class="n">ITASK</span><span class="p">,</span>
    <span class="mi">1</span>     <span class="n">ISTATE</span><span class="p">,</span><span class="n">IOPT</span><span class="p">,</span><span class="n">RWORK</span><span class="p">,</span><span class="n">LRW</span><span class="p">,</span><span class="n">IWORK</span><span class="p">,</span><span class="n">LIW</span><span class="p">,</span><span class="n">JAC</span><span class="p">,</span><span class="n">MF</span><span class="p">)</span>
       <span class="n">U1</span><span class="p">(</span><span class="n">IOUT</span><span class="p">)</span> <span class="o">=</span> <span class="n">U</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
       <span class="n">U2</span><span class="p">(</span><span class="n">IOUT</span><span class="p">)</span> <span class="o">=</span> <span class="n">U</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
       <span class="n">TOUT</span> <span class="o">=</span> <span class="n">TOUT</span> <span class="o">+</span> <span class="mf">2.0</span><span class="n">D</span><span class="o">-</span><span class="mi">1</span>
 <span class="mi">100</span> <span class="k">CONTINUE</span>
<span class="k">     END</span>

<span class="k">     SUBROUTINE </span><span class="n">F</span><span class="p">(</span><span class="n">NEQ</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">U</span><span class="p">,</span> <span class="n">UDOT</span><span class="p">)</span>
     <span class="kt">INTEGER </span><span class="n">NEQ</span>
     <span class="kt">DOUBLE PRECISION </span><span class="n">T</span><span class="p">,</span> <span class="n">U</span><span class="p">,</span> <span class="n">UDOT</span>
     <span class="k">DIMENSION </span><span class="n">U</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="n">UDOT</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
     <span class="n">UDOT</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">=</span> <span class="n">U</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
     <span class="n">UDOT</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">=</span> <span class="mf">3.0</span><span class="n">D0</span><span class="o">*</span><span class="p">(</span><span class="mf">1.0</span><span class="n">D0</span> <span class="o">-</span> <span class="n">U</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">U</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span><span class="o">*</span><span class="n">U</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">-</span> <span class="n">U</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
     <span class="k">RETURN</span>
<span class="k">     END</span>
</pre></div>
</div>
</div>
<div class="section" id="matlab">
<h4>MATLAB<a class="headerlink" href="#matlab" title="Permalink to this headline">¶</a></h4>
<p>The problem can be solved with very compact code in MATLAB. The definition
of the ODE system, the <span class="math">\(f(u,t)\)</span> function, is placed in
a function in a file, say <tt class="docutils literal"><span class="pre">myode.m</span></tt>:</p>
<div class="highlight-matlab"><div class="highlight"><pre><span class="k">function</span><span class="w"> </span>F <span class="p">=</span><span class="w"> </span><span class="nf">myode</span><span class="p">(</span>t, u<span class="p">);</span>
<span class="n">F</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span> <span class="p">=</span> <span class="n">u</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">F</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span> <span class="p">=</span> <span class="mi">3</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">u</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">u</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span><span class="o">*</span><span class="n">u</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">-</span> <span class="n">u</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</pre></div>
</div>
<p>In MATLAB we can then solve the problem by</p>
<div class="highlight-ipy"><div class="highlight"><pre><span class="o">&gt;&gt;</span> <span class="n">options</span> <span class="o">=</span> <span class="n">odeset</span><span class="p">(</span><span class="s">&#39;RelTol&#39;</span><span class="p">,</span><span class="mf">0.0</span><span class="p">,</span><span class="s">&#39;AbsTol&#39;</span><span class="p">,</span><span class="mf">1e-6</span><span class="p">);</span>
<span class="o">&gt;&gt;</span> <span class="n">tspan</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span> <span class="mi">30</span><span class="p">];</span>
<span class="o">&gt;&gt;</span> <span class="n">u0</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">;</span> <span class="mi">0</span><span class="p">]</span>
<span class="o">&gt;&gt;</span> <span class="p">[</span><span class="n">t</span><span class="p">,</span> <span class="n">u</span><span class="p">]</span> <span class="o">=</span> <span class="n">ode45</span><span class="p">(</span><span class="s">&#39;myode&#39;</span><span class="p">,</span> <span class="n">tspan</span><span class="p">,</span> <span class="n">u0</span><span class="p">,</span> <span class="n">options</span><span class="p">]);</span>
</pre></div>
</div>
</div>
<div class="section" id="python">
<h4>Python<a class="headerlink" href="#python" title="Permalink to this headline">¶</a></h4>
<p>Calling up the <tt class="docutils literal"><span class="pre">vode</span></tt> method from the <tt class="docutils literal"><span class="pre">scipy</span></tt> library in Python
also results in fairly compact code:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">u</span><span class="p">):</span>
    <span class="k">return</span> <span class="p">[</span><span class="n">u</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="mf">3.</span><span class="o">*</span><span class="p">(</span><span class="mf">1.</span> <span class="o">-</span> <span class="n">u</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="n">u</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span><span class="o">*</span><span class="n">u</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">u</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>

<span class="kn">from</span> <span class="nn">scipy.integrate</span> <span class="kn">import</span> <span class="n">ode</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">ode</span><span class="p">(</span><span class="n">f</span><span class="p">)</span><span class="o">.</span><span class="n">set_integrator</span><span class="p">(</span><span class="s">&#39;vode&#39;</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s">&#39;adams&#39;</span><span class="p">,</span>
                          <span class="n">order</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">rol</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">atol</span><span class="o">=</span><span class="mf">1e-6</span><span class="p">,</span>
                          <span class="n">with_jacobian</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
<span class="n">u0</span> <span class="o">=</span> <span class="p">[</span><span class="mf">2.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">]</span>
<span class="n">r</span><span class="o">.</span><span class="n">set_initial_value</span><span class="p">(</span><span class="n">u0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">T</span> <span class="o">=</span> <span class="mi">30</span>
<span class="n">dt</span> <span class="o">=</span> <span class="n">T</span><span class="o">/</span><span class="mf">150.</span>
<span class="n">u</span> <span class="o">=</span> <span class="p">[];</span>  <span class="n">t</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">while</span> <span class="n">r</span><span class="o">.</span><span class="n">successful</span><span class="p">()</span> <span class="ow">and</span> <span class="n">r</span><span class="o">.</span><span class="n">t</span> <span class="o">&lt;=</span> <span class="n">T</span><span class="p">:</span>
    <span class="n">r</span><span class="o">.</span><span class="n">integrate</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">t</span> <span class="o">+</span> <span class="n">dt</span><span class="p">)</span>
    <span class="n">u</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">y</span><span class="p">);</span>  <span class="n">t</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">t</span><span class="p">)</span>
</pre></div>
</div>
<p>Suppose you want to compare these methods and their implementations.
This requires three different main programs, but even worse: three
different implementations of the definition of the mathematical
problem.  Some specifications of <span class="math">\(f\)</span> has the signature <span class="math">\(f(u,t)\)</span> while
others require <span class="math">\(f(t,u)\)</span>, and such differences between packages are
often a cause of programming errors.</p>
</div>
</div>
<div class="section" id="odespy-s-unified-interface">
<h3>Odespy&#8217;s Unified Interface<a class="headerlink" href="#odespy-s-unified-interface" title="Permalink to this headline">¶</a></h3>
<p>The Odespy package provides a unified interface to all the three
mentioned types of methods, which makes it easy to run all of them
in a loop (program <a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/tutorial/src-odespy/motivation1.py">motivation.py</a>):</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
    <span class="k">return</span> <span class="p">[</span><span class="n">u</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="mf">3.</span><span class="o">*</span><span class="p">(</span><span class="mf">1.</span> <span class="o">-</span> <span class="n">u</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="n">u</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span><span class="o">*</span><span class="n">u</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">u</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>

<span class="n">u0</span> <span class="o">=</span> <span class="p">[</span><span class="mf">2.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">]</span>
<span class="kn">import</span> <span class="nn">odespy</span><span class="o">,</span> <span class="nn">numpy</span>

<span class="k">for</span> <span class="n">method</span> <span class="ow">in</span> <span class="n">odespy</span><span class="o">.</span><span class="n">Lsode</span><span class="p">,</span> <span class="n">odespy</span><span class="o">.</span><span class="n">DormandPrince</span><span class="p">,</span> <span class="n">odespy</span><span class="o">.</span><span class="n">Vode</span><span class="p">:</span>

    <span class="n">solver</span> <span class="o">=</span> <span class="n">method</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">rtol</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="n">atol</span><span class="o">=</span><span class="mf">1e-6</span><span class="p">,</span>
                    <span class="n">adams_or_bdf</span><span class="o">=</span><span class="s">&#39;adams&#39;</span><span class="p">,</span> <span class="n">order</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
    <span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">u0</span><span class="p">)</span>
    <span class="n">t_points</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">150</span><span class="p">)</span>
    <span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">t_points</span><span class="p">)</span>
</pre></div>
</div>
<p>Note in particular that the same <tt class="docutils literal"><span class="pre">f</span></tt> and the same call syntax can be
reused across methods and the underlying software.</p>
</div>
<div class="section" id="methods-and-implementations-offered-by-odespy">
<h3>Methods and Implementations Offered by Odespy<a class="headerlink" href="#methods-and-implementations-offered-by-odespy" title="Permalink to this headline">¶</a></h3>
<p>Odespy features a unified interface to the following collection of
numerical methods and implementations:</p>
<blockquote>
<div><ul class="simple">
<li>Pure Python implementations of classical explicit schemes such as
the Forward Euler method (also called Euler);
Runge-Kutta methods of 2nd, 3rd, and 4th order; Heun&#8217;s method;
Adams-Bashforth methods of 2nd, 3rd, and 4th order;
Adams-Bashforth-Moulton methods of 2nd and 3rd order.</li>
<li>Pure Python implementations of classical implicit schemes such as
Backward Euler; 2-step backward scheme; the <span class="math">\(\theta\)</span> rule;
the Midpoint (or Trapezoidal) method.</li>
<li>Pure Python implementations of adaptive explicit Runge-Kutta
methods of type Runge-Kutta-Fehlberg of order (4,5), Dormand-Prince
of order (4,5), Cash-Karp of order (4,5), Bogacki-Shampine of order (2,3).</li>
<li>Wrappers for all FORTRAN solvers in <a class="reference external" href="http://www.netlib.org/odepack/">ODEPACK</a>.</li>
<li>Wrappers for the wrappers of FORTRAN solvers in <a class="reference external" href="http://www.scipy.org">scipy</a>:
<tt class="docutils literal"><span class="pre">vode</span></tt> and <tt class="docutils literal"><span class="pre">zvode</span></tt> (adaptive Adams or BDF from <a class="reference external" href="http://www.netlib.org/ode/vode.f">vode.f</a>);
<tt class="docutils literal"><span class="pre">dopri5</span></tt> (adaptive Dormand-Prince method of order (4,5));
<tt class="docutils literal"><span class="pre">dop853</span></tt> (adaptive Dormand-Prince method of order 8(5,3));
<tt class="docutils literal"><span class="pre">odeint</span></tt> (adaptive switching between Adams or BDF from the implementation <tt class="docutils literal"><span class="pre">LSODA</span></tt> in <a class="reference external" href="http://www.netlib.org/odepack/">ODEPACK</a>).</li>
<li>Wrapper for the Runge-Kutta-Chebyshev formulas of order 2 as
offered by the well-known FORTRAN code <a class="reference external" href="http://www.netlib.org/ode/rkc.f">rkc.f</a>.</li>
<li>Wrapper for the Runge-Kutta-Fehlberg method of
order (4,5) as provided by the well-known FORTRAN code <a class="reference external" href="http://www.netlib.org/ode/rkf45.f">rkf45.f</a>.</li>
<li>Wrapper for the Radau5 method as provided by the well-known FORTRAN code
<a class="reference external" href="http://www.unige.ch/~hairer/prog/stiff/radau5.f">radau5.f</a>.</li>
<li>Wrapper for some solvers in the <a class="reference external" href="https://github.com/olivierverdier/odelab">odelab</a> package.</li>
</ul>
</div></blockquote>
<p>The ODE problem can always be specified in Python, but for wrappers of
FORTRAN codes one can also implement the problem in FORTRAN and avoid
callback to Python.</p>
</div>
</div>
<div class="section" id="installation">
<h2>Installation<a class="headerlink" href="#installation" title="Permalink to this headline">¶</a></h2>
<p>The Odespy package is most easily installed using <tt class="docutils literal"><span class="pre">pip</span></tt>:</p>
<div class="highlight-text"><div class="highlight"><pre>sudo pip install -e \
   git+https://github.com/hplgit/odespy.git#egg=odespy
</pre></div>
</div>
<p>Checking out the source code is almost as easy:</p>
<div class="highlight-text"><div class="highlight"><pre>git clone git@github.com:hplgit/odespy.git
cd odespy
sudo python setup.py install
</pre></div>
</div>
<p>You will at least also need Python v2.7 and the <a class="reference external" href="http://numpy.scipy.org/">numpy</a> package.  The FORTRAN codes <tt class="docutils literal"><span class="pre">rkc.f</span></tt>,
<tt class="docutils literal"><span class="pre">rkf45.f</span></tt>, <tt class="docutils literal"><span class="pre">radau5.f</span></tt>, and ODEPACK comes with Odespy and are compiled
and installed by <tt class="docutils literal"><span class="pre">setup.py</span></tt>. If you lack a FORTRAN compiler, you
can drop the installation of the FORTRAN solvers by running</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">sudo</span> <span class="n">python</span> <span class="n">setup</span><span class="o">.</span><span class="n">py</span> <span class="n">install</span> <span class="o">--</span><span class="n">no</span><span class="o">-</span><span class="n">fortran</span>
</pre></div>
</div>
<p>There have been various problems with compiling Odespy on Windows,
usually related to the Fortran compiler.
One recommended technique is to rely on Anaconda on Windows,
install the <tt class="docutils literal"><span class="pre">ming32</span></tt> compiler, and
then run</p>
<div class="highlight-text"><div class="highlight"><pre>Terminal&gt; python setup.py install build --compiler=ming32
</pre></div>
</div>
<p>This may give problems of the type</p>
<div class="highlight-text"><div class="highlight"><pre>File &quot;C:\Anaconda\lib\site-packages\numpy\distutils\fcompiler\gnu.py&quot;,
line 333, in get_libraries
raise NotImplementedError(...)
NotImplementedError: Only MS compiler supported with gfortran on win64
</pre></div>
</div>
<p>A remedy is to edit the <tt class="docutils literal"><span class="pre">gnu.py</span></tt> file and comment out the
<tt class="docutils literal"><span class="pre">NotImplementedError</span></tt>:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">else</span><span class="p">:</span>
    <span class="c">#raise NotImplementedError(&quot;Only MS compiler ...&quot;)</span>
    <span class="k">pass</span>
</pre></div>
</div>
<p>The Odespy package depends on several additional packages:</p>
<blockquote>
<div><ul class="simple">
<li><a class="reference external" href="http://scipy.org/">scipy</a> for running the <tt class="docutils literal"><span class="pre">Vode</span></tt> Adams/BDF solver, the
Dormand-Prince adaptive methods <tt class="docutils literal"><span class="pre">Dop853</span></tt>, and <tt class="docutils literal"><span class="pre">Dopri5</span></tt>,
and the <tt class="docutils literal"><span class="pre">scipy</span></tt> wrapper <tt class="docutils literal"><span class="pre">odeint</span></tt> of the FORTRAN code <tt class="docutils literal"><span class="pre">LSODA</span></tt>
(Odespy features an alternative wrapper of the latter, in class <tt class="docutils literal"><span class="pre">Lsoda</span></tt>).</li>
<li><a class="reference external" href="http://sympy.org/en/index.html">sympy</a> for running the
extremely accurate <tt class="docutils literal"><span class="pre">odefun_sympy</span></tt> solver.</li>
<li><a class="reference external" href="https://github.com/olivierverdier/odelab">odelab</a>
for accessing solvers in that package.</li>
</ul>
</div></blockquote>
<p>For plotting you will need <a class="reference external" href="http://matplotlib.sourceforge.net/">matplotlib</a> or <a class="reference external" href="https://github.com/hplgit/scitools/">scitools</a>.</p>
<p>These packages are readily downloaded and installed by the
standard <tt class="docutils literal"><span class="pre">setup.py</span></tt> script, as shown above.
On Ubuntu and other Debian-based Linux systems the following
line installs all that Odespy may need:</p>
<div class="highlight-text"><div class="highlight"><pre>sudo apt-get install python-scipy python-nose python-sympy \
     python-matplotlib python-scitools python-pip
</pre></div>
</div>
<p>The <tt class="docutils literal"><span class="pre">odelab</span></tt> package is installed by either</p>
<div class="highlight-text"><div class="highlight"><pre>pip install -e git+https://github.com/olivierverdier/odelab#egg=odelab
</pre></div>
</div>
<p>or downloading the source and running <tt class="docutils literal"><span class="pre">setup.py</span></tt>:</p>
<div class="highlight-text"><div class="highlight"><pre>git clone git://github.com/olivierverdier/odelab.git
cd odelab
sudo python setup.py install
</pre></div>
</div>
<div class="admonition-note admonition">
<p class="first admonition-title">Note</p>
<p class="last">Despite Odespy&#8217;s many dependencies on other software, you can run the
basic solvers implemented in pure Python without any additional software
packages.</p>
</div>
</div>
<div class="section" id="basic-usage">
<h2>Basic Usage<a class="headerlink" href="#basic-usage" title="Permalink to this headline">¶</a></h2>
<p>This section explains how to use Odespy. The general principles and
program steps are first explained. Thereafter, we present a series of
examples with progressive complexity with respect to Python constructs
and numerical methods.</p>
<div class="section" id="overview">
<h3>Overview<a class="headerlink" href="#overview" title="Permalink to this headline">¶</a></h3>
<p>A code using Odespy to solve ODEs consists of six steps. These are
outlined in generic form below.</p>
<div class="section" id="step-1">
<h4>Step 1<a class="headerlink" href="#step-1" title="Permalink to this headline">¶</a></h4>
<p><em>Write the ODE problem in generic form</em> <span class="math">\(u' = f(u, t)\)</span>,
where <span class="math">\(u(t)\)</span> is the unknown function to be solved for, or a vector
of unknown functions of time in case of a system of ODEs.</p>
</div>
<div class="section" id="step-2">
<h4>Step 2<a class="headerlink" href="#step-2" title="Permalink to this headline">¶</a></h4>
<p><em>Implement the right-hand side function</em> <span class="math">\(f(u, t)\)</span> as a Python function
<tt class="docutils literal"><span class="pre">f(u,</span> <span class="pre">t)</span></tt>.  The argument <tt class="docutils literal"><span class="pre">u</span></tt> is either a <tt class="docutils literal"><span class="pre">float</span></tt> object, in case of a
scalar ODE, or a <tt class="docutils literal"><span class="pre">numpy</span></tt> array object, in case of a system of ODEs.
Some solvers in this package also allow implementation of <span class="math">\(f\)</span> in
FORTRAN for increased efficiency.</p>
</div>
<div class="section" id="step-3">
<h4>Step 3<a class="headerlink" href="#step-3" title="Permalink to this headline">¶</a></h4>
<p><em>Create a solver object</em></p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">solver</span> <span class="o">=</span> <span class="n">classname</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
</pre></div>
</div>
<p>where <tt class="docutils literal"><span class="pre">classname</span></tt> is the name of a class in this package implementing
the desired numerical method.</p>
<p>Many solver classes has a range of parameters that the user can set to
control various parts of the solution process. The parameters are
documented in the doc string of the class (<tt class="docutils literal"><span class="pre">pydoc</span> <span class="pre">classname</span></tt> will list
the documentation in a terminal window). One can either specify parameters
at construction time, via extra keyword arguments to the constructor,</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">solver</span> <span class="o">=</span> <span class="n">classname</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">prm1</span><span class="o">=</span><span class="n">value1</span><span class="p">,</span> <span class="n">prm2</span><span class="o">=</span><span class="n">value2</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>
</pre></div>
</div>
<p>or at any time using the <tt class="docutils literal"><span class="pre">set</span></tt> method:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">solver</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">prm1</span><span class="o">=</span><span class="n">value1</span><span class="p">,</span> <span class="n">prm2</span><span class="o">=</span><span class="n">value2</span><span class="p">,</span> <span class="n">prm3</span><span class="o">=</span><span class="n">value3</span><span class="p">)</span>
<span class="o">...</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">prm4</span><span class="o">=</span><span class="n">value4</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="section" id="step-4">
<h4>Step 4<a class="headerlink" href="#step-4" title="Permalink to this headline">¶</a></h4>
<p><em>Set the initial condition</em> <span class="math">\(u(0)=U_0\)</span>,</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">U0</span><span class="p">)</span>
</pre></div>
</div>
<p>where <tt class="docutils literal"><span class="pre">U0</span></tt> is either a number, for a scalar ODE, or a sequence (list, tuple,
<tt class="docutils literal"><span class="pre">numpy</span></tt> array), for a system of ODEs.</p>
</div>
<div class="section" id="step-5">
<h4>Step 5<a class="headerlink" href="#step-5" title="Permalink to this headline">¶</a></h4>
<p><em>Solve the ODE problem</em>, which means to compute <span class="math">\(u(t)\)</span> at
some discrete user-specified time points <span class="math">\(t_1, t_2, \ldots, t_N\)</span>.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">T</span> <span class="o">=</span> <span class="o">...</span>  <span class="c"># end time</span>
<span class="n">time_points</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>
</pre></div>
</div>
<p>In case of a scalar ODE, the returned solution <tt class="docutils literal"><span class="pre">u</span></tt> is a one-dimensional
<tt class="docutils literal"><span class="pre">numpy</span></tt> array where <tt class="docutils literal"><span class="pre">u[i]</span></tt> holds the solution at time point <tt class="docutils literal"><span class="pre">t[i]</span></tt>.
For a system of ODEs, the returned <tt class="docutils literal"><span class="pre">u</span></tt> is a two-dimensional <tt class="docutils literal"><span class="pre">numpy</span></tt>
array where <tt class="docutils literal"><span class="pre">u[i,j]</span></tt> holds the solution of the <span class="math">\(j\)</span>-th unknown
function at the <span class="math">\(i\)</span>-th time point <tt class="docutils literal"><span class="pre">t[i]</span></tt> (<span class="math">\(u_j(t_i)\)</span> in mathematics
notation).</p>
<p>By giving the parameter <tt class="docutils literal"><span class="pre">disk_storage=True</span></tt> to the solver&#8217;s constructor,
the returned <tt class="docutils literal"><span class="pre">u</span></tt> array is memory mapped (i.e., of type <tt class="docutils literal"><span class="pre">numpy.memmap</span></tt>)
such that all the data are stored on file, but parts of the array can
be efficiently accessed.</p>
<p>The <tt class="docutils literal"><span class="pre">time_points</span></tt> array specifies the time points where we want the
solution to be computed. The returned array <tt class="docutils literal"><span class="pre">t</span></tt> is the same as
<tt class="docutils literal"><span class="pre">time_points</span></tt>.  The simplest numerical methods in the Odespy
package apply the <tt class="docutils literal"><span class="pre">time_points</span></tt> array directly for the time stepping.
That is, the time steps used are given by</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">time_points</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">time_points</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>  <span class="c">#  i=0,1,...,len(time_points)-1</span>
</pre></div>
</div>
<p>The adaptive schemes typically compute between each time point in
the <tt class="docutils literal"><span class="pre">time_points</span></tt> array, making this array a specification where
values of the unknowns are desired.</p>
<p>The <tt class="docutils literal"><span class="pre">solve</span></tt> method in solver classes also allows a second argument,
<tt class="docutils literal"><span class="pre">terminate</span></tt>, which is a user-implemented Python function specifying
when the solution process is to be terminated. For example,
terminating when the solution reaches an asymptotic (known) value
<tt class="docutils literal"><span class="pre">a</span></tt> can be done by</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">terminate</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">step_no</span><span class="p">):</span>
    <span class="c"># u and t are arrays. Most recent solution is u[step_no].</span>
    <span class="n">tolerance</span> <span class="o">=</span> <span class="mf">1E-6</span>
    <span class="k">return</span> <span class="nb">abs</span><span class="p">(</span><span class="n">u</span><span class="p">[</span><span class="n">step_no</span><span class="p">]</span> <span class="o">-</span> <span class="n">a</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">tolerance</span>

<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">,</span> <span class="n">terminate</span><span class="p">)</span>
</pre></div>
</div>
<p>The arguments transferred to the <tt class="docutils literal"><span class="pre">terminate</span></tt> function are the
solution array <tt class="docutils literal"><span class="pre">u</span></tt>, the corresponding time points <tt class="docutils literal"><span class="pre">t</span></tt>, and
an integer <tt class="docutils literal"><span class="pre">step_no</span></tt> reflecting the most recently computed <tt class="docutils literal"><span class="pre">u</span></tt>
value. That is, <tt class="docutils literal"><span class="pre">u[step_no]</span></tt> is most recently computed value of <span class="math">\(u\)</span>.
(The array data <tt class="docutils literal"><span class="pre">u[step_no+1:]</span></tt> will typically be zero as these
are uncomputed future values.)</p>
</div>
<div class="section" id="step-6">
<h4>Step 6<a class="headerlink" href="#step-6" title="Permalink to this headline">¶</a></h4>
<p><em>Extract solution components</em> for plotting and further analysis.
Since the <tt class="docutils literal"><span class="pre">u</span></tt> array returned from <tt class="docutils literal"><span class="pre">solver.solve</span></tt> stores all unknown
functions at all discrete time levels, one usually wants to extract
individual unknowns as one-dimensional arrays. Here is an example
where unknown number <span class="math">\(0\)</span> and <span class="math">\(k\)</span> are extracted in individual arrays
and plotted:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">u_0</span> <span class="o">=</span> <span class="n">u</span><span class="p">[:,</span><span class="mi">0</span><span class="p">]</span>
<span class="n">u_k</span> <span class="o">=</span> <span class="n">u</span><span class="p">[:,</span><span class="n">k</span><span class="p">]</span>

<span class="kn">from</span> <span class="nn">matplotlib.pyplot</span> <span class="kn">import</span> <span class="n">plot</span><span class="p">,</span> <span class="n">show</span>
<span class="n">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">u_0</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">u_k</span><span class="p">)</span>
<span class="n">show</span><span class="p">()</span>
</pre></div>
</div>
</div>
</div>
<div class="section" id="first-example-logistic-growth">
<span id="ode-sec-exgr"></span><h3>First Example: Logistic Growth<a class="headerlink" href="#first-example-logistic-growth" title="Permalink to this headline">¶</a></h3>
<p>Our first example concerns the simple scalar ODE problem</p>
<div class="math">
\[\frac{du}{dt}=au\left(1-\frac{u}{R}\right),\quad u(0)=A,\]</div>
<p>where <span class="math">\(A&gt;0\)</span>, <span class="math">\(a&gt;0\)</span>, and <span class="math">\(R&gt;0\)</span> are known constants. This is
a common model for population dynamics in ecology where <span class="math">\(u\)</span> is the
number of individuals, <span class="math">\(a\)</span> the initial growth rate, <span class="math">\(R\)</span> is the
maximum number of individuals that the environment allows (the so-called
<em>carrying capacity</em> of the environment).</p>
<p>Using a standard
Runge-Kutta method of order four, the code for solving the problem in
the time interval <span class="math">\([0,10]\)</span> with <span class="math">\(N=30\)</span> time steps, looks like this
(program <a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/tutorial/src-odespy/logistic1.py">logistic1.py</a>):</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">a</span><span class="o">*</span><span class="n">u</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">u</span><span class="o">/</span><span class="n">R</span><span class="p">)</span>

<span class="n">a</span> <span class="o">=</span> <span class="mi">2</span>
<span class="n">R</span> <span class="o">=</span> <span class="mf">1E+5</span>
<span class="n">A</span> <span class="o">=</span> <span class="mi">1</span>

<span class="kn">import</span> <span class="nn">odespy</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RK4</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">A</span><span class="p">)</span>

<span class="kn">from</span> <span class="nn">numpy</span> <span class="kn">import</span> <span class="n">linspace</span><span class="p">,</span> <span class="n">exp</span>
<span class="n">T</span> <span class="o">=</span> <span class="mi">10</span>  <span class="c"># end of simulation</span>
<span class="n">N</span> <span class="o">=</span> <span class="mi">30</span>  <span class="c"># no of time steps</span>
<span class="n">time_points</span> <span class="o">=</span> <span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>
</pre></div>
</div>
<p>With the <tt class="docutils literal"><span class="pre">RK4</span></tt> method and other non-adaptive methods
the time steps are dictated by the <tt class="docutils literal"><span class="pre">time_points</span></tt> array.
A constant time step of size is implied in the present example.
Running an alternative numerical method just means replacing <tt class="docutils literal"><span class="pre">RK4</span></tt> by, e.g.,
<tt class="docutils literal"><span class="pre">RK2</span></tt>, <tt class="docutils literal"><span class="pre">ForwardEuler</span></tt>, <tt class="docutils literal"><span class="pre">BackwardEuler</span></tt>, <tt class="docutils literal"><span class="pre">AdamsBashforth2</span></tt>, etc.</p>
<p>We can easily plot the numerical solution and compare with the exact
solution (which is known for this equation):</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">u_exact</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">R</span><span class="o">*</span><span class="n">A</span><span class="o">*</span><span class="n">exp</span><span class="p">(</span><span class="n">a</span><span class="o">*</span><span class="n">t</span><span class="p">)</span><span class="o">/</span><span class="p">(</span><span class="n">R</span> <span class="o">+</span> <span class="n">A</span><span class="o">*</span><span class="p">(</span><span class="n">exp</span><span class="p">(</span><span class="n">a</span><span class="o">*</span><span class="n">t</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span>

<span class="kn">from</span> <span class="nn">matplotlib.pyplot</span> <span class="kn">import</span> <span class="o">*</span>

<span class="n">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="s">&#39;r-&#39;</span><span class="p">,</span>
     <span class="n">t</span><span class="p">,</span> <span class="n">u_exact</span><span class="p">(</span><span class="n">t</span><span class="p">),</span> <span class="s">&#39;bo&#39;</span><span class="p">)</span>
<span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmppng&#39;</span><span class="p">);</span> <span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp.pdf&#39;</span><span class="p">)</span>
<span class="n">show</span><span class="p">()</span>
</pre></div>
</div>
<div class="figure">
<a class="reference internal image-reference" href="_images/logistic1.png"><img alt="_images/logistic1.png" src="_images/logistic1.png" style="width: 400px;" /></a>
<p class="caption">Solution of the logistic equation with the 4-th order Runge-Kutta method (solid line) and comparison with the exact solution (dots)</p>
</div>
<p>All the examples in this tutorial are found in the GitHub directory
<a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/tutorial/src-odespy/">https://github.com/hplgit/odespy/blob/master/doc/src/tutorial/src-odespy/</a>.
If you download the tarball or clone the GitHub repository, the examples
reside in the directory <tt class="docutils literal"><span class="pre">doc/src/odespy/src-odespy</span></tt>.</p>
</div>
<div class="section" id="parameters-in-the-right-hand-side-function">
<span id="ode-sec-exgr-farg"></span><h3>Parameters in the Right-Hand Side Function<a class="headerlink" href="#parameters-in-the-right-hand-side-function" title="Permalink to this headline">¶</a></h3>
<p>The right-hand side function and all physical parameters are often
lumped together in a class, for instance,</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Logistic</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">A</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="n">a</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">R</span> <span class="o">=</span> <span class="n">R</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">A</span> <span class="o">=</span> <span class="n">A</span>

    <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="n">a</span><span class="p">,</span> <span class="n">R</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">R</span>  <span class="c"># short form</span>
        <span class="k">return</span> <span class="n">a</span><span class="o">*</span><span class="n">u</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">u</span><span class="o">/</span><span class="n">R</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">u_exact</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="n">a</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">A</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">R</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">A</span>  <span class="c"># short form</span>
        <span class="k">return</span> <span class="n">R</span><span class="o">*</span><span class="n">A</span><span class="o">*</span><span class="n">exp</span><span class="p">(</span><span class="n">a</span><span class="o">*</span><span class="n">t</span><span class="p">)</span><span class="o">/</span><span class="p">(</span><span class="n">R</span> <span class="o">+</span> <span class="n">A</span><span class="o">*</span><span class="p">(</span><span class="n">exp</span><span class="p">(</span><span class="n">a</span><span class="o">*</span><span class="n">t</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span>
</pre></div>
</div>
<p>Note that introducing local variables like <tt class="docutils literal"><span class="pre">a</span></tt> and <tt class="docutils literal"><span class="pre">R</span></tt>, instead of
using <tt class="docutils literal"><span class="pre">self.a</span></tt> and <tt class="docutils literal"><span class="pre">self.A</span></tt>, makes the code closer to the mathematics.
This can be convenient when proof reading the implementation of
complicated ODEs.</p>
<p>The numerical solution is computed by</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">odespy</span>
<span class="n">problem</span> <span class="o">=</span> <span class="n">Logistic</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">R</span><span class="o">=</span><span class="mf">1E+5</span><span class="p">,</span> <span class="n">A</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RK4</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">A</span><span class="p">)</span>

<span class="n">T</span> <span class="o">=</span> <span class="mi">10</span>  <span class="c"># end of simulation</span>
<span class="n">N</span> <span class="o">=</span> <span class="mi">30</span>  <span class="c"># no of time steps</span>
<span class="n">time_points</span> <span class="o">=</span> <span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>
</pre></div>
</div>
<p>The complete program is available in the file program <a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/logistic2.py">logistic2.py</a>.</p>
<p>Instead of having the problem parameters <tt class="docutils literal"><span class="pre">a</span></tt> and <tt class="docutils literal"><span class="pre">R</span></tt> in the ODE as
global variables or in a class, we may include them as extra arguments
to <tt class="docutils literal"><span class="pre">f</span></tt>, either as positional arguments or as keyword
arguments. Positional arguments can be sent to <tt class="docutils literal"><span class="pre">f</span></tt> via the constructor
argument <tt class="docutils literal"><span class="pre">f_args</span></tt> (a list/tuple of variables), while a dictionary
<tt class="docutils literal"><span class="pre">f_kwargs</span></tt> is used to transfer keyword arguments to <tt class="docutils literal"><span class="pre">f</span></tt> via the
constructor. Here is an example on using keyword arguments:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">R</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">a</span><span class="o">*</span><span class="n">u</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">u</span><span class="o">/</span><span class="n">R</span><span class="p">)</span>

<span class="n">A</span> <span class="o">=</span> <span class="mi">1</span>

<span class="kn">import</span> <span class="nn">odespy</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RK4</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">f_kwargs</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">R</span><span class="o">=</span><span class="mf">1E+5</span><span class="p">))</span>
</pre></div>
</div>
<p>In general, a mix
of positional and keyword arguments can be used in <tt class="docutils literal"><span class="pre">f</span></tt>:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">arg3</span><span class="p">,</span> <span class="o">...</span><span class="p">,</span> <span class="n">kwarg1</span><span class="o">=</span><span class="n">val1</span><span class="p">,</span> <span class="n">kwarg2</span><span class="o">=</span><span class="n">val2</span><span class="p">,</span> <span class="o">...</span><span class="p">):</span>
    <span class="o">...</span>

<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">classname</span><span class="p">(</span><span class="n">f</span><span class="p">,</span>
                    <span class="n">f_args</span><span class="o">=</span><span class="p">[</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">arg3</span><span class="p">,</span> <span class="o">...</span><span class="p">],</span>
                    <span class="n">f_kwargs</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">kwarg1</span><span class="o">=</span><span class="n">val1</span><span class="p">,</span> <span class="n">kwarg2</span><span class="o">=</span><span class="n">val2</span><span class="p">,</span> <span class="o">...</span><span class="p">))</span>

<span class="c"># Alternative setting of f_args and f_kwargs</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">f_args</span><span class="o">=</span><span class="p">[</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">arg3</span><span class="p">,</span> <span class="o">...</span><span class="p">],</span>
           <span class="n">f_kwargs</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">kwarg1</span><span class="o">=</span><span class="n">val1</span><span class="p">,</span> <span class="n">kwarg2</span><span class="o">=</span><span class="n">val2</span><span class="p">,</span> <span class="o">...</span><span class="p">))</span>
</pre></div>
</div>
<p>Solvers will call <tt class="docutils literal"><span class="pre">f</span></tt> as <tt class="docutils literal"><span class="pre">f(u,</span> <span class="pre">t,</span> <span class="pre">*f_args,</span> <span class="pre">**f_kwargs)</span></tt>.</p>
</div>
<div class="section" id="continuing-a-previous-simulation">
<h3>Continuing a Previous Simulation<a class="headerlink" href="#continuing-a-previous-simulation" title="Permalink to this headline">¶</a></h3>
<p>It is easy to simulate for some time interval <span class="math">\([0, T_1]\)</span>,
then continue with <span class="math">\(u(T_1)\)</span> as new initial condition and simulate for
<span class="math">\(t\)</span> in <span class="math">\([T_1, T_2]\)</span> and so on. Let us divide the time
domain into subdomains and compute the solution for
each subdomain in sequence. The following program performs the steps
(<a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/logistic4.py">logistic4.py</a>).</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">a</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">R</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">a</span><span class="o">*</span><span class="n">u</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">u</span><span class="o">/</span><span class="n">R</span><span class="p">)</span>

<span class="n">A</span> <span class="o">=</span> <span class="mi">1</span>

<span class="kn">import</span> <span class="nn">odespy</span><span class="o">,</span> <span class="nn">numpy</span>
<span class="kn">from</span> <span class="nn">matplotlib.pyplot</span> <span class="kn">import</span> <span class="n">plot</span><span class="p">,</span> <span class="n">hold</span><span class="p">,</span> <span class="n">show</span><span class="p">,</span> <span class="n">axis</span>

<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RK4</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">f_kwargs</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">R</span><span class="o">=</span><span class="mf">1E+5</span><span class="p">))</span>

<span class="c"># Split time domain into subdomains and</span>
<span class="c"># integrate the ODE in each subdomain</span>
<span class="n">T</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">12</span><span class="p">]</span>     <span class="c"># subdomain boundaries</span>

<span class="n">N_tot</span> <span class="o">=</span> <span class="mi">30</span>               <span class="c"># total no of time steps</span>
<span class="n">dt</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">T</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span><span class="o">/</span><span class="n">N_tot</span>  <span class="c"># time step, kept fixed</span>
<span class="n">u</span> <span class="o">=</span> <span class="p">[];</span> <span class="n">t</span> <span class="o">=</span> <span class="p">[]</span>           <span class="c"># collectors for u and t in each domain</span>

<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">T</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">):</span>
    <span class="n">T_interval</span> <span class="o">=</span> <span class="n">T</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">T</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
    <span class="n">N</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">round</span><span class="p">(</span><span class="n">T_interval</span><span class="o">/</span><span class="n">dt</span><span class="p">))</span>
    <span class="n">time_points</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="n">T</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">T</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">],</span> <span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>

    <span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">A</span><span class="p">)</span>  <span class="c"># at time_points[0]</span>
    <span class="k">print</span> <span class="s">&#39;Solving in [</span><span class="si">%s</span><span class="s">, </span><span class="si">%s</span><span class="s">] with </span><span class="si">%d</span><span class="s"> intervals&#39;</span> <span class="o">%</span> \
          <span class="p">(</span><span class="n">T</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">T</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">],</span> <span class="n">N</span><span class="p">)</span>
    <span class="n">ui</span><span class="p">,</span> <span class="n">ti</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>
    <span class="n">A</span> <span class="o">=</span> <span class="n">ui</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>  <span class="c"># newest ui value is next initial condition</span>

    <span class="n">plot</span><span class="p">(</span><span class="n">ti</span><span class="p">,</span> <span class="n">ui</span><span class="p">)</span>
    <span class="n">hold</span><span class="p">(</span><span class="s">&#39;on&#39;</span><span class="p">)</span>

    <span class="n">u</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ui</span><span class="p">);</span>  <span class="n">t</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ti</span><span class="p">)</span>

<span class="n">axis</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="o">-</span><span class="mf">0.1E+5</span><span class="p">,</span> <span class="mf">1.1E+5</span><span class="p">])</span>
<span class="c"># Can concatenate all the elements of u and t, if desired</span>
<span class="n">u</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">concatenate</span><span class="p">(</span><span class="n">u</span><span class="p">);</span>  <span class="n">t</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">concatenate</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmppng&#39;</span><span class="p">);</span> <span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp.pdf&#39;</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="section" id="termination-criterion-for-the-simulation">
<h3>Termination Criterion for the Simulation<a class="headerlink" href="#termination-criterion-for-the-simulation" title="Permalink to this headline">¶</a></h3>
<p>We know that the solution <span class="math">\(u\)</span> of the logistic equation approaches
<span class="math">\(R\)</span> as <span class="math">\(t\rightarrow\infty\)</span>. Instead of
using a trial and error process for determining an appropriate
time integral for integration, the <tt class="docutils literal"><span class="pre">solver.solve</span></tt> method accepts
a user-defined function <tt class="docutils literal"><span class="pre">terminate</span></tt> that can be used to implement
a criterion for terminating the solution process.
Mathematically, the relevant criterion is
<span class="math">\(||u-R|| &lt; \hbox{tol}\)</span>, where tol is an acceptable
tolerance, say <span class="math">\(100\)</span> in the present case where <span class="math">\(R=10^5\)</span>.
The <tt class="docutils literal"><span class="pre">terminate</span></tt> function implements the criterion
and returns true if the criterion is met:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">terminate</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">step_no</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;u[step_no] holds (the most recent) solution at t[step_no].&quot;&quot;&quot;</span>
    <span class="k">return</span> <span class="nb">abs</span><span class="p">(</span><span class="n">u</span><span class="p">[</span><span class="n">step_no</span><span class="p">]</span> <span class="o">-</span> <span class="n">R</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">tol</span>
</pre></div>
</div>
<p>Note that the simulation is anyway stopped for <span class="math">\(t &gt; T\)</span> so <span class="math">\(T\)</span>
must be large enough for the termination criterion to be reached (if not,
a warning will be issued).
With a <tt class="docutils literal"><span class="pre">terminate</span></tt> function it is also convenient to specify the
time step <tt class="docutils literal"><span class="pre">dt</span></tt> and not the total number of time steps.</p>
<p>A complete program can be as follows (<a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/logistic5.py">logistic5.py</a>):</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">a</span><span class="o">*</span><span class="n">u</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">u</span><span class="o">/</span><span class="n">R</span><span class="p">)</span>

<span class="n">a</span> <span class="o">=</span> <span class="mi">2</span>
<span class="n">R</span> <span class="o">=</span> <span class="mf">1E+5</span>
<span class="n">A</span> <span class="o">=</span> <span class="mi">1</span>

<span class="kn">import</span> <span class="nn">odespy</span><span class="o">,</span> <span class="nn">numpy</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RK4</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">A</span><span class="p">)</span>

<span class="n">T</span> <span class="o">=</span> <span class="mi">20</span>  <span class="c"># end of simulation</span>
<span class="n">dt</span> <span class="o">=</span> <span class="mf">0.25</span>
<span class="n">N</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">round</span><span class="p">(</span><span class="n">T</span><span class="o">/</span><span class="n">dt</span><span class="p">))</span>
<span class="n">time_points</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>

<span class="n">tol</span> <span class="o">=</span> <span class="mi">100</span>   <span class="c"># tolerance for termination criterion</span>

<span class="k">def</span> <span class="nf">terminate</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">step_no</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;u[step_no] holds (the most recent) solution at t[step_no].&quot;&quot;&quot;</span>
    <span class="k">return</span> <span class="nb">abs</span><span class="p">(</span><span class="n">u</span><span class="p">[</span><span class="n">step_no</span><span class="p">]</span> <span class="o">-</span> <span class="n">R</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">tol</span>

<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">,</span> <span class="n">terminate</span><span class="p">)</span>
<span class="k">print</span> <span class="s">&#39;Final u(t=</span><span class="si">%g</span><span class="s">)=</span><span class="si">%g</span><span class="s">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="n">u</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>

<span class="kn">from</span> <span class="nn">matplotlib.pyplot</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="s">&#39;r-&#39;</span><span class="p">)</span>
<span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmppng&#39;</span><span class="p">);</span> <span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp.pdf&#39;</span><span class="p">)</span>
<span class="n">show</span><span class="p">()</span>
</pre></div>
</div>
</div>
<div class="section" id="a-class-based-implementation">
<h3>A Class-Based Implementation<a class="headerlink" href="#a-class-based-implementation" title="Permalink to this headline">¶</a></h3>
<p>The previous code example can be recast into a more class-based
(&#8220;object-oriented programming&#8221;) example. We lump all data related
to the problem (the &#8220;physics&#8221;) into a problem class <tt class="docutils literal"><span class="pre">Logistic</span></tt>, while
all data related to the numerical solution and its quality are
taken care of by class <tt class="docutils literal"><span class="pre">Solver</span></tt>. The code below illustrates
the ideas (<a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/logistic26py">logistic6.py</a>):</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="kn">as</span> <span class="nn">plt</span>
<span class="kn">import</span> <span class="nn">odespy</span>

<span class="k">class</span> <span class="nc">Logistic</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">A</span><span class="p">,</span> <span class="n">T</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">        a` is (initial growth rate), `R` the carrying capacity,</span>
<span class="sd">        `A` the initial amount of u, and `T` is some (very) total</span>
<span class="sd">        simulation time when `u` is very close to the asymptotic</span>
<span class="sd">        value `R`.</span>
<span class="sd">        &quot;&quot;&quot;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">R</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">A</span> <span class="o">=</span> <span class="n">a</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">A</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">tol</span> <span class="o">=</span> <span class="mf">0.01</span><span class="o">*</span><span class="n">R</span> <span class="c"># tolerance for termination criterion</span>

    <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Right-hand side of the ODE.&quot;&quot;&quot;</span>
        <span class="n">a</span><span class="p">,</span> <span class="n">R</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">R</span>  <span class="c"># short form</span>
        <span class="k">return</span> <span class="n">a</span><span class="o">*</span><span class="n">u</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">u</span><span class="o">/</span><span class="n">R</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">terminate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">step_no</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;u[step_no] holds solution at t[step_no].&quot;&quot;&quot;</span>
        <span class="k">return</span> <span class="nb">abs</span><span class="p">(</span><span class="n">u</span><span class="p">[</span><span class="n">step_no</span><span class="p">]</span> <span class="o">-</span> <span class="bp">self</span><span class="o">.</span><span class="n">R</span><span class="p">)</span> <span class="o">&lt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">tol</span>

    <span class="k">def</span> <span class="nf">u_exact</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="n">a</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">A</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">R</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">A</span>  <span class="c"># short form</span>
        <span class="k">return</span> <span class="n">R</span><span class="o">*</span><span class="n">A</span><span class="o">*</span><span class="n">np</span><span class="o">.</span><span class="n">exp</span><span class="p">(</span><span class="n">a</span><span class="o">*</span><span class="n">t</span><span class="p">)</span><span class="o">/</span><span class="p">(</span><span class="n">R</span> <span class="o">+</span> <span class="n">A</span><span class="o">*</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">exp</span><span class="p">(</span><span class="n">a</span><span class="o">*</span><span class="n">t</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span>


<span class="k">class</span> <span class="nc">Solver</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">problem</span><span class="p">,</span> <span class="n">dt</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s">&#39;RK4&#39;</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">problem</span> <span class="o">=</span> <span class="n">problem</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">dt</span> <span class="o">=</span> <span class="n">dt</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">method_class</span> <span class="o">=</span> <span class="nb">eval</span><span class="p">(</span><span class="s">&#39;odespy.&#39;</span> <span class="o">+</span> <span class="n">method</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">N</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">round</span><span class="p">(</span><span class="n">T</span><span class="o">/</span><span class="n">dt</span><span class="p">))</span>

    <span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">solver</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">method_class</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">problem</span><span class="o">.</span><span class="n">A</span><span class="p">)</span>
        <span class="n">time_points</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">problem</span><span class="o">.</span><span class="n">T</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">u</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">t</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span>
            <span class="n">time_points</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">problem</span><span class="o">.</span><span class="n">terminate</span><span class="p">)</span>
        <span class="k">print</span> <span class="s">&#39;Final u(t=</span><span class="si">%g</span><span class="s">)=</span><span class="si">%g</span><span class="s">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="n">u</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>

    <span class="k">def</span> <span class="nf">plot</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">t</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">u</span><span class="p">,</span> <span class="s">&#39;r-&#39;</span><span class="p">,</span>
                 <span class="bp">self</span><span class="o">.</span><span class="n">t</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">u_exact</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">t</span><span class="p">),</span> <span class="s">&#39;bo&#39;</span><span class="p">)</span>
        <span class="n">plt</span><span class="o">.</span><span class="n">legend</span><span class="p">([</span><span class="s">&#39;numerical&#39;</span><span class="p">,</span> <span class="s">&#39;exact&#39;</span><span class="p">])</span>
        <span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp.png&#39;</span><span class="p">);</span> <span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp.pdf&#39;</span><span class="p">)</span>
        <span class="n">plt</span><span class="o">.</span><span class="n">show</span><span class="p">()</span>

<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">problem</span> <span class="o">=</span> <span class="n">Logistic</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">R</span><span class="o">=</span><span class="mf">1E+5</span><span class="p">,</span> <span class="n">A</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">T</span><span class="o">=</span><span class="mi">20</span><span class="p">)</span>
    <span class="n">solver</span> <span class="o">=</span> <span class="n">Solver</span><span class="p">(</span><span class="n">problem</span><span class="p">,</span> <span class="n">dt</span><span class="o">=</span><span class="mf">0.25</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s">&#39;RK4&#39;</span><span class="p">)</span>
    <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">()</span>
    <span class="n">solver</span><span class="o">.</span><span class="n">plot</span><span class="p">()</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span>
</pre></div>
</div>
</div>
<div class="section" id="using-other-symbols">
<h3>Using Other Symbols<a class="headerlink" href="#using-other-symbols" title="Permalink to this headline">¶</a></h3>
<p>The Odespy package applies <tt class="docutils literal"><span class="pre">u</span></tt> for the unknown function or vector of
unknown functions and <tt class="docutils literal"><span class="pre">t</span></tt> as the name of the independent
variable. Many problems involve other symbols for functions and
independent variables. These symbols should be reflected in the user&#8217;s
code.  For example, here is a coding example involving the logistic
equation written as <span class="math">\(y'(x)=au(x)(1-u(x)/R(x))\)</span>, where now a variable
<span class="math">\(R=R(x)\)</span> is considered. Following the setup from the very first
program above solving the logistic ODE, we can easily introduce our
own nomenclature (<a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/logistic7.py">logistic7.py</a>):</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">a</span><span class="o">*</span><span class="n">y</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">y</span><span class="o">/</span><span class="n">R</span><span class="p">)</span>

<span class="n">a</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>  <span class="n">R</span> <span class="o">=</span> <span class="mf">1E+5</span><span class="p">;</span>  <span class="n">A</span> <span class="o">=</span> <span class="mi">1</span>

<span class="kn">import</span> <span class="nn">odespy</span><span class="o">,</span> <span class="nn">numpy</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RK4</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">A</span><span class="p">)</span>

<span class="n">L</span> <span class="o">=</span> <span class="mi">10</span>  <span class="c"># end of x domain</span>
<span class="n">N</span> <span class="o">=</span> <span class="mi">30</span>  <span class="c"># no of time steps</span>
<span class="n">x_points</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
<span class="n">y</span><span class="p">,</span> <span class="n">x</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">x_points</span><span class="p">)</span>

<span class="kn">from</span> <span class="nn">matplotlib.pyplot</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">plot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="s">&#39;r-&#39;</span><span class="p">)</span>
<span class="n">xlabel</span><span class="p">(</span><span class="s">&#39;x&#39;</span><span class="p">);</span> <span class="n">ylabel</span><span class="p">(</span><span class="s">&#39;y&#39;</span><span class="p">)</span>
<span class="n">show</span><span class="p">()</span>
</pre></div>
</div>
<p>As shown, we use <tt class="docutils literal"><span class="pre">y</span></tt> for <tt class="docutils literal"><span class="pre">u</span></tt>, <tt class="docutils literal"><span class="pre">x</span></tt> for <tt class="docutils literal"><span class="pre">t</span></tt>, and <tt class="docutils literal"><span class="pre">x_points</span></tt> instead
of <tt class="docutils literal"><span class="pre">time_points</span></tt>.</p>
</div>
<div class="section" id="example-solving-an-ode-system">
<span id="ode-sec-ex-osc"></span><h3>Example: Solving an ODE System<a class="headerlink" href="#example-solving-an-ode-system" title="Permalink to this headline">¶</a></h3>
<p>We shall now explain how to solve a system of ODEs using a scalar
second-order ODE as starting point.
The angle <span class="math">\(\theta\)</span> of a pendulum with mass <span class="math">\(m\)</span> and length <span class="math">\(L\)</span>
is governed by the equation
(neglecting air resistance for simplicity)</p>
<div class="math">
\[mL\ddot\theta + mg\sin\theta = 0,\quad \theta (0)=\Theta,\
\dot\theta (0)=0 .\]</div>
<p>A dot over <span class="math">\(\theta\)</span> implies differentiation with respect to time.
The ODE can be written as <span class="math">\(\ddot\theta + c\sin\theta=0\)</span> by
introducing <span class="math">\(c = g/L\)</span>.</p>
<p>This problem must be expressed as a first-order ODE system if it is
going to be solved by the tools in the Odespy package.
Introducing <span class="math">\(\omega = \dot\theta\)</span> (the angular velocity) as auxiliary
unknown, we get the system</p>
<div class="math">
\[\begin{split}\dot\theta &amp;= \omega,\\
\dot\omega &amp;= -c\sin\theta,\end{split}\]</div>
<p>with <span class="math">\(\theta(0)=\Theta\)</span> and <span class="math">\(\omega(0)=0\)</span>.</p>
<p>Now the <tt class="docutils literal"><span class="pre">f</span></tt> function must return a list or array with the two
right-hand side functions:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
    <span class="n">theta</span><span class="p">,</span> <span class="n">omega</span> <span class="o">=</span> <span class="n">u</span>
    <span class="k">return</span> <span class="p">[</span><span class="n">omega</span><span class="p">,</span> <span class="o">-</span><span class="n">c</span><span class="o">*</span><span class="n">sin</span><span class="p">(</span><span class="n">theta</span><span class="p">)]</span>
</pre></div>
</div>
<p>Note that when we have a system of ODEs with <tt class="docutils literal"><span class="pre">n</span></tt> components, the <tt class="docutils literal"><span class="pre">u</span></tt>
object sent to the <tt class="docutils literal"><span class="pre">f</span></tt> function is an array of length <tt class="docutils literal"><span class="pre">n</span></tt>,
representing the value of all components in the ODE system at time <tt class="docutils literal"><span class="pre">t</span></tt>.
Here we extract the two components of <tt class="docutils literal"><span class="pre">u</span></tt> in separate local variables
with names equal to what is used in the mathematical description of
the current problem.</p>
<p>The initial conditions must be specified as a list:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">Heun</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">([</span><span class="n">Theta</span><span class="p">,</span> <span class="mi">0</span><span class="p">])</span>
</pre></div>
</div>
<p>To specify the time points we here first decide on a number of periods
(oscillations back and forth) to simulate and then on the time resolution
of each period. Note that when <span class="math">\(\Theta\)</span> is small we can replace
<span class="math">\(\sin\theta\)</span> by <span class="math">\(\theta\)</span> and find an analytical
solution
<span class="math">\(\theta (t)=\Theta\cos\left(\sqrt{c}t\right)\)</span>
whose period is <span class="math">\(2\pi/\sqrt{c}\)</span>. We use this expression
as an approximation for the period also when <span class="math">\(\Theta\)</span> is not
small.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">freq</span> <span class="o">=</span> <span class="n">sqrt</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>      <span class="c"># frequency of oscillations when Theta is small</span>
<span class="n">period</span> <span class="o">=</span> <span class="mi">2</span><span class="o">*</span><span class="n">pi</span><span class="o">/</span><span class="n">freq</span>  <span class="c"># the period of the oscillations</span>
<span class="n">T</span> <span class="o">=</span> <span class="mi">10</span><span class="o">*</span><span class="n">period</span>       <span class="c"># final time</span>
<span class="n">N_per_period</span> <span class="o">=</span> <span class="mi">20</span>   <span class="c"># resolution of one period</span>
<span class="n">N</span> <span class="o">=</span> <span class="n">N_per_period</span><span class="o">*</span><span class="n">period</span>
<span class="n">time_points</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>

<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>
</pre></div>
</div>
<p>The <tt class="docutils literal"><span class="pre">u</span></tt> returned from <tt class="docutils literal"><span class="pre">solver.solve</span></tt> is a two-dimensional array, where the
columns hold the various solution functions of the ODE system. That is,
the first column holds <span class="math">\(\theta\)</span> and the second column holds
<span class="math">\(\omega\)</span>. For convenience we extract the individual solution
components in individual arrays:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">theta</span> <span class="o">=</span> <span class="n">u</span><span class="p">[:,</span><span class="mi">0</span><span class="p">]</span>
<span class="n">omega</span> <span class="o">=</span> <span class="n">u</span><span class="p">[:,</span><span class="mi">1</span><span class="p">]</span>

<span class="kn">from</span> <span class="nn">matplotlib.pyplot</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">theta</span><span class="p">,</span> <span class="s">&#39;r-&#39;</span><span class="p">)</span>
<span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmppng&#39;</span><span class="p">);</span> <span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp.pdf&#39;</span><span class="p">)</span>
<span class="n">show</span><span class="p">()</span>
</pre></div>
</div>
<p>The complete program is available in the file <a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/osc1a.py">osc1a.py</a>.</p>
<p>Looking at the plot reveals that the numerical solution has
an alarming feature: the amplitude grows (indicating increasing
energy in the system). Changing <tt class="docutils literal"><span class="pre">T</span></tt> to 28 periods instead of 10
makes the numerical solution explode.
The increasing amplitude is a numerical artifact that some of the simple
solution methods suffer from.</p>
<div class="figure">
<a class="reference internal image-reference" href="_images/osc1a.png"><img alt="_images/osc1a.png" src="_images/osc1a.png" style="width: 500px;" /></a>
<p class="caption">Heun&#8217;s method used to simulate oscillations of a pendulum</p>
</div>
<p>Using a more sophisticated method, say the 4-th order Runge-Kutta method,
is just a matter of substituting <tt class="docutils literal"><span class="pre">Heun</span></tt> by <tt class="docutils literal"><span class="pre">RK4</span></tt>:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RK4</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">([</span><span class="n">Theta</span><span class="p">,</span> <span class="mi">0</span><span class="p">])</span>
<span class="n">freq</span> <span class="o">=</span> <span class="n">sqrt</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>      <span class="c"># frequency of oscillations when Theta is small</span>
<span class="n">period</span> <span class="o">=</span> <span class="mi">2</span><span class="o">*</span><span class="n">pi</span><span class="o">/</span><span class="n">freq</span>  <span class="c"># the period of the oscillations</span>
<span class="n">T</span> <span class="o">=</span> <span class="mi">10</span><span class="o">*</span><span class="n">period</span>       <span class="c"># final time</span>
<span class="n">N_per_period</span> <span class="o">=</span> <span class="mi">20</span>   <span class="c"># resolution of one period</span>
<span class="n">N</span> <span class="o">=</span> <span class="n">N_per_period</span><span class="o">*</span><span class="n">period</span>
<span class="n">time_points</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>

<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>

<span class="n">theta</span> <span class="o">=</span> <span class="n">u</span><span class="p">[:,</span><span class="mi">0</span><span class="p">]</span>
<span class="n">omega</span> <span class="o">=</span> <span class="n">u</span><span class="p">[:,</span><span class="mi">1</span><span class="p">]</span>

<span class="kn">from</span> <span class="nn">matplotlib.pyplot</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">theta</span><span class="p">,</span> <span class="s">&#39;r-&#39;</span><span class="p">)</span>
<span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmppng&#39;</span><span class="p">);</span> <span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp.pdf&#39;</span><span class="p">)</span>
<span class="n">show</span><span class="p">()</span>
</pre></div>
</div>
<p>The amplitude now becomes (almost) constant in time as expected.
Another very good and popular method for this problem is presented next.</p>
</div>
<div class="section" id="the-euler-cromer-method">
<h3>The Euler-Cromer Method<a class="headerlink" href="#the-euler-cromer-method" title="Permalink to this headline">¶</a></h3>
<p>Physicists will most likely
solve the model problem in the section <a class="reference internal" href="#ode-sec-ex-osc"><em>Example: Solving an ODE System</em></a> by
the Euler-Cromer method. For a single degree of freedom system,</p>
<div class="math">
\[\ddot x + f(x, \dot x, t) = 0,\]</div>
<p>typically modeling an oscillatory system, the Euler-Cromer method
writes the system as two ODEs,</p>
<div class="math">
\[\begin{split}\dot v &amp;= -g(x, v, t),\\
\dot x &amp;= v\thinspace .\end{split}\]</div>
<p>A Forward Euler scheme is used for the first equation, while a Backward
Euler scheme is used for the second:</p>
<div class="math">
\[\begin{split}v^{n+1} &amp;= v^n - \Delta t\, g(x^n, v^n, n\Delta t),\\
x^{n+1} &amp;= x^n + \Delta t\,v^{n+1}\thinspace .\end{split}\]</div>
<p>The two first-order methods used in this symmetric fashion results in
a second-order method that will preserve the amplitude of the oscillations.</p>
<p>For general multi degree of freedom systems, we have some vector ODE
arising from, typically, Newton&#8217;s second law of motion,</p>
<div class="math">
\[\ddot{\boldsymbol{r}} + \boldsymbol{g}(\boldsymbol{r},\dot{\boldsymbol{r}},t) = 0\thinspace .\]</div>
<p>This is rewritten as</p>
<div class="math">
\[\begin{split}\dot{\boldsymbol{v}} &amp;= -\boldsymbol{g}(\boldsymbol{r}, \boldsymbol{v}, t),\\
\dot{\boldsymbol{r}} &amp;= \boldsymbol{v},\end{split}\]</div>
<p>and discretized as</p>
<div class="math">
\[\begin{split}\boldsymbol{v}^{n+1} &amp;= \boldsymbol{v}^n -\Delta t\,\boldsymbol{g}(\boldsymbol{r}^n, \boldsymbol{v}^n, t),\\
\boldsymbol{r}^{n+1} &amp;= \boldsymbol{r}^n + \Delta t\,\boldsymbol{v}^{n+1}\thinspace .\end{split}\]</div>
<p>The convention in Odespy is to group all the unknowns as velocity and position
for each degree of freedom. That is, if the component form of <span class="math">\(\boldsymbol{r}\)</span>
and <span class="math">\(\boldsymbol{v}\)</span> is written as</p>
<div class="math">
\[\boldsymbol{r} = (r^{(0)}, r^{(0)}, \ldots, r^{(N)}),
\quad \boldsymbol{v} = (v^{(0)}, v^{(0)}, \ldots, v^{(N)}),\]</div>
<p>the <span class="math">\(u\)</span> vector of all unknowns in the Euler-Cromer method in Odespy
must be</p>
<div class="math">
\[u=(v^{(0)}, r^{(0)}, v^{(1)}, r^{(1)}, \ldots,v^{(N)}, r^{(N)})\thinspace .\]</div>
<p>The corresponding set of ODEs are</p>
<div class="math">
\[\begin{split}\dot v^{(0)} &amp;= -g^{(0)}(\boldsymbol{r}, \boldsymbol{v}, t),\\
\dot r^{(0)} &amp;= v^{(0)},\\
\dot v^{(1)} &amp;= -g^{(1)}(\boldsymbol{r}, \boldsymbol{v}, t),\\
\dot r^{(1)} &amp;= v^{(1)},\\
&amp;\cdots\\
\dot v^{(N)} &amp;= -g^{(N)}(\boldsymbol{r}, \boldsymbol{v}, t),\\
\dot r^{(N)} &amp;= v^{(N)}\thinspace .\end{split}\]</div>
<p>For the particular case of a pendulum we write our system as</p>
<div class="math">
\[\begin{split}\dot\omega &amp;= -c\sin\theta,\\
\dot\theta &amp;= \omega,\end{split}\]</div>
<p>and let <span class="math">\(u=(\omega, \theta)\)</span>. The relevant right-hand side function becomes</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
    <span class="n">omega</span><span class="p">,</span> <span class="n">theta</span> <span class="o">=</span> <span class="n">u</span>
    <span class="k">return</span> <span class="p">[</span><span class="o">-</span><span class="n">c</span><span class="o">*</span><span class="n">sin</span><span class="p">(</span><span class="n">theta</span><span class="p">),</span> <span class="n">omega</span><span class="p">]</span>
</pre></div>
</div>
<p>With some imports,</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">odespy</span>
<span class="kn">from</span> <span class="nn">numpy</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">matplotlib.pyplot</span> <span class="kn">import</span> <span class="o">*</span>
</pre></div>
</div>
<p>we can write the rest of the program in a standard fashion:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">c</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">Theta0_degrees</span> <span class="o">=</span> <span class="mi">30</span>

<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">EulerCromer</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">Theta0</span> <span class="o">=</span> <span class="n">Theta0_degrees</span><span class="o">*</span><span class="n">pi</span><span class="o">/</span><span class="mi">180</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="n">Theta0</span><span class="p">])</span>
<span class="c"># Solve for num_periods periods using formulas for small theta</span>
<span class="n">freq</span> <span class="o">=</span> <span class="n">sqrt</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>          <span class="c"># frequency of oscillations</span>
<span class="n">period</span> <span class="o">=</span> <span class="mi">2</span><span class="o">*</span><span class="n">pi</span><span class="o">/</span><span class="n">freq</span>      <span class="c"># one period</span>
<span class="n">N</span> <span class="o">=</span> <span class="mi">40</span>                  <span class="c"># intervals per period</span>
<span class="n">dt</span> <span class="o">=</span> <span class="n">period</span><span class="o">/</span><span class="n">N</span>           <span class="c"># time step</span>
<span class="n">num_periods</span> <span class="o">=</span> <span class="mi">10</span>
<span class="n">T</span> <span class="o">=</span> <span class="n">num_periods</span><span class="o">*</span><span class="n">period</span>  <span class="c"># total simulation time</span>

<span class="n">time_points</span> <span class="o">=</span> <span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">num_periods</span><span class="o">*</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>

<span class="c"># Extract components and plot theta</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">u</span><span class="p">[:,</span><span class="mi">1</span><span class="p">]</span>
<span class="n">omega</span> <span class="o">=</span> <span class="n">u</span><span class="p">[:,</span><span class="mi">0</span><span class="p">]</span>
<span class="n">theta_linear</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">t</span><span class="p">:</span> <span class="n">Theta0</span><span class="o">*</span><span class="n">cos</span><span class="p">(</span><span class="n">sqrt</span><span class="p">(</span><span class="n">c</span><span class="p">)</span><span class="o">*</span><span class="n">t</span><span class="p">)</span>
<span class="n">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">theta</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">theta_linear</span><span class="p">(</span><span class="n">t</span><span class="p">))</span>
<span class="n">legend</span><span class="p">([</span><span class="s">&#39;Euler-Cromer&#39;</span><span class="p">,</span> <span class="s">&#39;Linearized problem&#39;</span><span class="p">],</span> <span class="n">loc</span><span class="o">=</span><span class="s">&#39;lower left&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>With <span class="math">\(\Theta_0\)</span> as 30 degrees, the fully nonlinear solution is
slightly out of phase with the solution of the linearized
problem <span class="math">\(\ddot\theta + c\theta =0\)</span>, see Figure <a class="reference internal" href="#ode-sec-ec-fig1"><em>Euler-Cromer method applied to the pendulum problem</em></a>.
As <span class="math">\(\Theta_0\rightarrow 0\)</span>, the two curves approach each other.
The Euler-Cromer method is significantly better than Heun&#8217;s method
used in the previous section and reproduces the exact amplitude.</p>
<div class="figure" id="ode-sec-ec-fig1">
<a class="reference internal image-reference" href="_images/osc_EC_30.png"><img alt="_images/osc_EC_30.png" src="_images/osc_EC_30.png" style="width: 500px;" /></a>
<p class="caption"><em>Euler-Cromer method applied to the pendulum problem</em></p>
</div>
</div>
<div class="section" id="testing-several-methods">
<h3>Testing Several Methods<a class="headerlink" href="#testing-several-methods" title="Permalink to this headline">¶</a></h3>
<p>[<strong>hpl 1</strong>: After Euler-Cromer, change the order of the ODEs and unknowns!]</p>
<p>We shall now make a more advanced solver by
extending the pendulum example. More specifically, we shall</p>
<blockquote>
<div><ul class="simple">
<li>represent the right-hand side function as class,</li>
<li>compare several different solvers,</li>
<li>compute error of numerical solutions.</li>
</ul>
</div></blockquote>
<p>Since we want to compare numerical errors in the various
solvers we need a test problem where the exact solution is known.
Approximating <span class="math">\(\sin(\theta)\)</span> by <span class="math">\(\theta\)</span>
(valid for small <span class="math">\(\theta\)</span>), gives the ODE system</p>
<div class="math">
\[\begin{split}\dot\theta &amp;= \omega,\\
\dot\omega &amp;= -c\theta,\end{split}\]</div>
<p>with <span class="math">\(\theta(0)=\Theta\)</span> and <span class="math">\(\omega(0)=0\)</span>.</p>
<p>Right-hand side functions with parameters can be handled by
including extra arguments via the <tt class="docutils literal"><span class="pre">f_args</span></tt> and <tt class="docutils literal"><span class="pre">f_kwargs</span></tt> functionality,
or by using a class where the parameters are attributes and
an <tt class="docutils literal"><span class="pre">f</span></tt> method defines <span class="math">\(f(u,t)\)</span>.
The section <a class="reference internal" href="#ode-sec-exgr-farg"><em>Parameters in the Right-Hand Side Function</em></a> exemplifies the details.
A minimal class representation of the right-hand side
function in the present case looks like this:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Problem</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="n">Theta</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">c</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">Theta</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">c</span><span class="p">),</span> <span class="nb">float</span><span class="p">(</span><span class="n">Theta</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="n">theta</span><span class="p">,</span> <span class="n">omega</span> <span class="o">=</span> <span class="n">u</span><span class="p">;</span>  <span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">c</span>
        <span class="k">return</span> <span class="p">[</span><span class="n">omega</span><span class="p">,</span> <span class="o">-</span><span class="n">c</span><span class="o">*</span><span class="n">theta</span><span class="p">]</span>

<span class="n">problem</span> <span class="o">=</span> <span class="n">Problem</span><span class="p">(</span><span class="n">c</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">Theta</span><span class="o">=</span><span class="n">pi</span><span class="o">/</span><span class="mi">4</span><span class="p">)</span>
</pre></div>
</div>
<p>It would be convenient to add an attribute <tt class="docutils literal"><span class="pre">period</span></tt> which holds
an estimate of the period of oscillations as we need this for
deciding on the complete time interval for solving the differential
equations. An appropriate extension of class <tt class="docutils literal"><span class="pre">Problem</span></tt> is therefore</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Problem</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="n">Theta</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">c</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">Theta</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">c</span><span class="p">),</span> <span class="nb">float</span><span class="p">(</span><span class="n">Theta</span><span class="p">)</span>

        <span class="bp">self</span><span class="o">.</span><span class="n">freq</span> <span class="o">=</span> <span class="n">sqrt</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">period</span> <span class="o">=</span> <span class="mi">2</span><span class="o">*</span><span class="n">pi</span><span class="o">/</span><span class="bp">self</span><span class="o">.</span><span class="n">freq</span>

    <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="n">theta</span><span class="p">,</span> <span class="n">omega</span> <span class="o">=</span> <span class="n">u</span><span class="p">;</span>  <span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">c</span>
        <span class="k">return</span> <span class="p">[</span><span class="n">omega</span><span class="p">,</span> <span class="o">-</span><span class="n">c</span><span class="o">*</span><span class="n">theta</span><span class="p">]</span>

<span class="n">problem</span> <span class="o">=</span> <span class="n">Problem</span><span class="p">(</span><span class="n">c</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">Theta</span><span class="o">=</span><span class="n">pi</span><span class="o">/</span><span class="mi">4</span><span class="p">)</span>
</pre></div>
</div>
<p>The second extension is to loop over many solvers. All
solvers can be listed by</p>
<div class="highlight-ipy"><div class="highlight"><pre><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">odespy</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">methods</span> <span class="o">=</span> <span class="n">list_all_solvers</span><span class="p">()</span>
<span class="o">&gt;&gt;&gt;</span> <span class="k">for</span> <span class="n">method</span> <span class="ow">in</span> <span class="n">methods</span><span class="p">:</span>
<span class="o">...</span>   <span class="k">print</span> <span class="n">method</span>
<span class="o">...</span>
<span class="n">AdamsBashMoulton2</span>
<span class="n">AdamsBashMoulton3</span>
<span class="n">AdamsBashforth2</span>
<span class="o">...</span>
<span class="n">Vode</span>
<span class="n">lsoda_scipy</span>
<span class="n">odefun_sympy</span>
<span class="n">odelab</span>
</pre></div>
</div>
<p>A similar function, <tt class="docutils literal"><span class="pre">list_available_solvers</span></tt>, returns a list of the
names of the solvers that are available in the current installation
(e.g., the <tt class="docutils literal"><span class="pre">Vode</span></tt> solver is only available if the comprehensive
<tt class="docutils literal"><span class="pre">scipy</span></tt> package is installed).
This is the list that is usually most relevant.</p>
<p>For now we explicitly choose a subset of the commonly available solvers:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">odespy</span>
<span class="n">solvers</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">odespy</span><span class="o">.</span><span class="n">ThetaRule</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">,</span> <span class="n">theta</span><span class="o">=</span><span class="mi">0</span><span class="p">),</span>   <span class="c"># Forward Euler</span>
    <span class="n">odespy</span><span class="o">.</span><span class="n">ThetaRule</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">,</span> <span class="n">theta</span><span class="o">=</span><span class="mf">0.5</span><span class="p">),</span> <span class="c"># Midpoint method</span>
    <span class="n">odespy</span><span class="o">.</span><span class="n">ThetaRule</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">,</span> <span class="n">theta</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span>   <span class="c"># Backward Euler</span>
    <span class="n">odespy</span><span class="o">.</span><span class="n">RK4</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">),</span>
    <span class="n">odespy</span><span class="o">.</span><span class="n">MidpointIter</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">,</span> <span class="n">max_iter</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">eps_iter</span><span class="o">=</span><span class="mf">0.01</span><span class="p">),</span>
    <span class="n">odespy</span><span class="o">.</span><span class="n">LeapfrogFiltered</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">),</span>
    <span class="p">]</span>
</pre></div>
</div>
<p>To see what a method is and its arguments to the constructor, invoke
the doc string of the class, e.g., <tt class="docutils literal"><span class="pre">help(ThetaRule)</span></tt> inside a
Python shell like IPython, or run <tt class="docutils literal"><span class="pre">pydoc</span> <span class="pre">odespy.ThetaRule</span></tt> in a
terminal window, or invoke the <a class="reference external" href="http://hplgit.github.com/odespy/doc/api/">Odespy API documentation</a>.</p>
<p>It will be evident that the <tt class="docutils literal"><span class="pre">ThetaRule</span></tt> solver with <tt class="docutils literal"><span class="pre">theta=0</span></tt> and
<tt class="docutils literal"><span class="pre">theta=1</span></tt> (Forward and Backward Euler methods) gives growing and
decaying amplitudes, respectively, while the other solvers are
capable of reproducing the constant amplitude of the oscillations of
in the current mathematical model.</p>
<p>The loop over the chosen solvers may look like</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">N_per_period</span> <span class="o">=</span> <span class="mi">20</span>
<span class="n">T</span> <span class="o">=</span> <span class="mi">3</span><span class="o">*</span><span class="n">problem</span><span class="o">.</span><span class="n">period</span>   <span class="c"># final time</span>
<span class="kn">import</span> <span class="nn">numpy</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="kn">as</span> <span class="nn">plt</span>
<span class="n">legends</span> <span class="o">=</span> <span class="p">[]</span>

<span class="k">for</span> <span class="n">solver</span> <span class="ow">in</span> <span class="n">solvers</span><span class="p">:</span>
    <span class="n">solver_name</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">solver</span><span class="p">)</span>  <span class="c"># short description of solver</span>
    <span class="k">print</span> <span class="n">solver_name</span>

    <span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">([</span><span class="n">problem</span><span class="o">.</span><span class="n">Theta</span><span class="p">,</span> <span class="mi">0</span><span class="p">])</span>
    <span class="n">N</span> <span class="o">=</span> <span class="n">N_per_period</span><span class="o">*</span><span class="n">problem</span><span class="o">.</span><span class="n">period</span>
    <span class="n">time_points</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>

    <span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>

    <span class="n">theta</span> <span class="o">=</span> <span class="n">u</span><span class="p">[:,</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">legends</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">solver_name</span><span class="p">)</span>
    <span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">theta</span><span class="p">)</span>
    <span class="n">plt</span><span class="o">.</span><span class="n">hold</span><span class="p">(</span><span class="s">&#39;on&#39;</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">legend</span><span class="p">(</span><span class="n">legends</span><span class="p">)</span>
<span class="n">plotfile</span> <span class="o">=</span> <span class="n">__file__</span><span class="p">[:</span><span class="o">-</span><span class="mi">3</span><span class="p">]</span>
<span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="n">plotfile</span> <span class="o">+</span> <span class="s">&#39;.png&#39;</span><span class="p">);</span> <span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="n">plotfile</span> <span class="o">+</span> <span class="s">&#39;.pdf&#39;</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">show</span><span class="p">()</span>
</pre></div>
</div>
<p>A complete program is available as <a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/osc2.py">osc2.py</a>.</p>
<div class="figure">
<a class="reference internal image-reference" href="_images/osc2.png"><img alt="_images/osc2.png" src="_images/osc2.png" style="width: 500px;" /></a>
<p class="caption">Comparison of methods for solving the ODE system for a pendulum</p>
</div>
<p>We can extend this program to compute the error in each numerical
solution for different time step sizes.
Let <tt class="docutils literal"><span class="pre">results</span></tt> be a dictionary with the method name as
key, containing two sub dictionaries <tt class="docutils literal"><span class="pre">dt</span></tt> and <tt class="docutils literal"><span class="pre">error</span></tt>, which hold
a sequence of time steps and a sequence of corresponding
errors, respectively. The errors are computed by subtracting
the numerical solution from the exact solution,</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">theta_exact</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">t</span><span class="p">:</span> <span class="n">problem</span><span class="o">.</span><span class="n">Theta</span><span class="o">*</span><span class="n">numpy</span><span class="o">.</span><span class="n">cos</span><span class="p">(</span><span class="n">sqrt</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">c</span><span class="p">)</span><span class="o">*</span><span class="n">t</span><span class="p">)</span>
<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">u</span><span class="p">[:,</span><span class="mi">0</span><span class="p">]</span>
<span class="n">error</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">abs</span><span class="p">(</span><span class="n">theta_exact</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> <span class="o">-</span> <span class="n">theta</span><span class="p">)</span>
</pre></div>
</div>
<p>The so-called L2 norm of the <tt class="docutils literal"><span class="pre">error</span></tt> array is a suitable
scalar error measure (square root of total error squared and integrated,
here numerically):</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">error_L2</span> <span class="o">=</span> <span class="n">sqrt</span><span class="p">(</span><span class="n">numpy</span><span class="o">.</span><span class="n">sum</span><span class="p">(</span><span class="n">error</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">/</span><span class="n">dt</span><span class="p">)</span>
</pre></div>
</div>
<p>where <tt class="docutils literal"><span class="pre">dt</span></tt> is the time step size.</p>
<p>Typical loops over solvers and resolutions look as follows (<a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/osc3.py">osc3.py</a>):</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">try</span><span class="p">:</span>
    <span class="n">num_periods</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span>
    <span class="n">num_periods</span> <span class="o">=</span> <span class="mi">8</span>  <span class="c"># default</span>

<span class="n">T</span> <span class="o">=</span> <span class="n">num_periods</span><span class="o">*</span><span class="n">problem</span><span class="o">.</span><span class="n">period</span>       <span class="c"># final time</span>
<span class="n">results</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">resolutions</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">80</span><span class="p">,</span> <span class="mi">160</span><span class="p">]</span>  <span class="c"># intervals per period</span>
<span class="kn">import</span> <span class="nn">numpy</span>

<span class="k">for</span> <span class="n">solver</span> <span class="ow">in</span> <span class="n">solvers</span><span class="p">:</span>
    <span class="n">solver_name</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">solver</span><span class="p">)</span>
    <span class="n">results</span><span class="p">[</span><span class="n">solver_name</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;dt&#39;</span><span class="p">:</span> <span class="p">[],</span> <span class="s">&#39;error&#39;</span><span class="p">:</span> <span class="p">[]}</span>

    <span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">([</span><span class="n">problem</span><span class="o">.</span><span class="n">Theta</span><span class="p">,</span> <span class="mi">0</span><span class="p">])</span>

    <span class="k">for</span> <span class="n">N_per_period</span> <span class="ow">in</span> <span class="n">resolutions</span><span class="p">:</span>
        <span class="n">N</span> <span class="o">=</span> <span class="n">N_per_period</span><span class="o">*</span><span class="n">num_periods</span>
        <span class="n">time_points</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>

        <span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>

        <span class="n">theta</span> <span class="o">=</span> <span class="n">u</span><span class="p">[:,</span><span class="mi">0</span><span class="p">]</span>
        <span class="n">error</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">abs</span><span class="p">(</span><span class="n">theta_exact</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> <span class="o">-</span> <span class="n">theta</span><span class="p">)</span>
        <span class="n">error_L2</span> <span class="o">=</span> <span class="n">sqrt</span><span class="p">(</span><span class="n">numpy</span><span class="o">.</span><span class="n">sum</span><span class="p">(</span><span class="n">error</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">/</span><span class="n">N</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">numpy</span><span class="o">.</span><span class="n">isnan</span><span class="p">(</span><span class="n">error_L2</span><span class="p">):</span>  <span class="c"># drop nan (overflow)</span>
            <span class="n">results</span><span class="p">[</span><span class="n">solver_name</span><span class="p">][</span><span class="s">&#39;dt&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">t</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
            <span class="n">results</span><span class="p">[</span><span class="n">solver_name</span><span class="p">][</span><span class="s">&#39;error&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">error_L2</span><span class="p">)</span>
</pre></div>
</div>
<p>Assuming the error to be of the form <span class="math">\(C\Delta t^r\)</span>, we can estimate
<span class="math">\(C\)</span> and <span class="math">\(r\)</span> from two consecutive experiments to obtain a sequence
of <span class="math">\(r\)</span> values which (hopefully) convergences to a value that we can
view as the empirical convergence rate of a method.
Given the sequence of time steps and errors, we can compare two
experiments <span class="math">\(i\)</span> and <span class="math">\(i-1\)</span>, with errors <span class="math">\(E_{i}=C\Delta t_i^r\)</span>, and
estimate <span class="math">\(r=\ln(E_i/E_{i-1})/\ln(\Delta t_i/\Delta t_{i-1})\)</span>:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">log</span>
<span class="k">print</span> <span class="s">&#39;</span><span class="se">\n\n</span><span class="s">Convergence results for </span><span class="si">%d</span><span class="s"> periods&#39;</span> <span class="o">%</span> <span class="n">num_periods</span>
<span class="k">for</span> <span class="n">solver_name</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
    <span class="n">r_h</span> <span class="o">=</span> <span class="n">results</span><span class="p">[</span><span class="n">solver_name</span><span class="p">][</span><span class="s">&#39;dt&#39;</span><span class="p">]</span>
    <span class="n">r_E</span> <span class="o">=</span> <span class="n">results</span><span class="p">[</span><span class="n">solver_name</span><span class="p">][</span><span class="s">&#39;error&#39;</span><span class="p">]</span>
    <span class="n">rates</span> <span class="o">=</span> <span class="p">[</span><span class="n">log</span><span class="p">(</span><span class="n">r_E</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">/</span><span class="n">r_E</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span><span class="o">/</span><span class="n">log</span><span class="p">(</span><span class="n">r_h</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">/</span><span class="n">r_h</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span> <span class="k">for</span> <span class="n">i</span>
             <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">r_h</span><span class="p">))]</span>
    <span class="c"># Reformat rates with 1 decimal for rate</span>
    <span class="n">rates</span> <span class="o">=</span> <span class="s">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="s">&#39;</span><span class="si">%.1f</span><span class="s">&#39;</span> <span class="o">%</span> <span class="n">rate</span> <span class="k">for</span> <span class="n">rate</span> <span class="ow">in</span> <span class="n">rates</span><span class="p">])</span>
    <span class="k">print</span> <span class="s">&#39;</span><span class="si">%-20s</span><span class="s"> r: </span><span class="si">%s</span><span class="s"> E_min=</span><span class="si">%.1E</span><span class="s">&#39;</span> <span class="o">%</span> \
          <span class="p">(</span><span class="n">solver_name</span><span class="p">,</span> <span class="n">rates</span><span class="p">,</span> <span class="nb">min</span><span class="p">(</span><span class="n">results</span><span class="p">[</span><span class="n">solver_name</span><span class="p">][</span><span class="s">&#39;error&#39;</span><span class="p">]))</span>
</pre></div>
</div>
<p>With 4 periods we get</p>
<div class="highlight-text"><div class="highlight"><pre>ThetaRule(theta=0)   r: 4.2,  2.4, 1.7, 1.3 E_min=1.9E-01
LeapfrogFiltered     r: 10.3, 0.3, 0.5, 0.7 E_min=1.8E-01
ThetaRule(theta=1)   r: 0.2,  0.4, 0.6, 0.8 E_min=1.3E-01
ThetaRule            r: 2.3,  2.0, 2.0, 2.0 E_min=2.1E-03
RK4                  r: 4.0,  4.0, 4.0, 4.0 E_min=1.6E-07
Leapfrog             r: 2.2,  2.0, 2.0, 2.0 E_min=2.1E-03
RK2                  r: 2.3,  2.0, 2.0, 2.0 E_min=2.1E-03
MidpointIter         r: 2.0,  1.0, 2.0, 2.0 E_min=2.1E-03
</pre></div>
</div>
<p>The rates of the Forward and Backward Euler methods (1st and 3rd line) have
not yet converged to unity, as expected, while the 2nd-order
Runge-Kutta method, Leapfrog, and the <span class="math">\(\theta\)</span> rule with
<span class="math">\(\theta =0.5\)</span>
(<tt class="docutils literal"><span class="pre">ThetaRule</span></tt> with default value of <tt class="docutils literal"><span class="pre">theta</span></tt>) shows the expected
<span class="math">\(r=2\)</span> value. The 4th-order Runge-Kutta holds the promise of being of 4th
order, while the filtered Leapfrog method has slow convergence and
a fairly large error, which is also evident in the previous figure.</p>
<p>Extending the time domain to 20 periods makes many of the
simplest methods inaccurate and the rates computed on coarse
time meshes are irrelevant. Also in this case, three of the methods
are useless, while the others deliver their promised convergence
rates (Forward Euler, i.e., <tt class="docutils literal"><span class="pre">ThetaRule</span></tt> with <tt class="docutils literal"><span class="pre">theta=0</span></tt> is left
out because of ridiculous results):</p>
<div class="highlight-text"><div class="highlight"><pre>LeapfrogFiltered     r: 63.7, 0.0, 0.1, 0.2 E_min=4.3E-01
ThetaRule(theta=1)   r: 0.0,  0.1, 0.1, 0.3 E_min=3.8E-01
ThetaRule            r: 3.7,  2.1, 2.0, 2.0 E_min=1.0E-02
RK4                  r: 4.0,  4.0, 4.0, 4.0 E_min=8.0E-07
Leapfrog             r: 0.5,  1.9, 2.0, 2.0 E_min=1.0E-02
RK2                  r: 3.7,  2.1, 2.0, 2.0 E_min=1.0E-02
MidpointIter         r: 1.8,  0.8, 1.9, 2.0 E_min=1.0E-02
</pre></div>
</div>
</div>
</div>
<div class="section" id="more-advanced-implementations">
<h2>More Advanced Implementations<a class="headerlink" href="#more-advanced-implementations" title="Permalink to this headline">¶</a></h2>
<div class="section" id="make-a-subclass-of-class-problem">
<h3>Make a Subclass of Class Problem<a class="headerlink" href="#make-a-subclass-of-class-problem" title="Permalink to this headline">¶</a></h3>
<p>Odespy features a module <tt class="docutils literal"><span class="pre">problems</span></tt> for defining ODE problems.
There is a superclass <tt class="docutils literal"><span class="pre">Problem</span></tt> in this module defining what we
expect of information about an ODE problem, as well as some
convenience functions that are inherited in subclasses.
A rough sketch of class <tt class="docutils literal"><span class="pre">Problem</span></tt> is listed here:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Problem</span><span class="p">:</span>
    <span class="n">stiff</span> <span class="o">=</span> <span class="bp">False</span>    <span class="c"># classification of the problem is stiff or not</span>
    <span class="n">complex_</span> <span class="o">=</span> <span class="bp">False</span> <span class="c"># True if f(u,t) is complex valued</span>
    <span class="n">not_suitable_solvers</span> <span class="o">=</span> <span class="p">[]</span>  <span class="c"># list solvers that should be be used</span>
    <span class="n">short_description</span> <span class="o">=</span> <span class="s">&#39;&#39;</span>     <span class="c"># one-line problem description</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">pass</span>

    <span class="k">def</span> <span class="nf">__contains__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attr</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Return True if attr is a method in instance self.&quot;&quot;&quot;</span>

    <span class="k">def</span> <span class="nf">terminate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">step_number</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Default terminate function, always returning False.&quot;&quot;&quot;</span>
        <span class="k">return</span> <span class="bp">False</span>

    <span class="k">def</span> <span class="nf">default_parameters</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">        Compute suitable time_points, atol/rtol, etc. for the</span>
<span class="sd">        particular problem. Useful for quick generation of test</span>
<span class="sd">        cases, demos, unit tests, etc.</span>
<span class="sd">        &quot;&quot;&quot;</span>
        <span class="k">return</span> <span class="p">{}</span>

    <span class="k">def</span> <span class="nf">u_exact</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Implementation of the exact solution.&quot;&quot;&quot;</span>
        <span class="k">return</span> <span class="bp">None</span>
</pre></div>
</div>
<p>Subclasses of <tt class="docutils literal"><span class="pre">Problem</span></tt> typically implements the constructor, for
registering parameters in the ODE and the initial condition, and
a method <tt class="docutils literal"><span class="pre">f</span></tt> for defining the right-hand side. For implicit solution method
we may provide a method <tt class="docutils literal"><span class="pre">jac</span></tt> returning the Jacobian of <span class="math">\(f(u,t)\)</span> with
respect to <span class="math">\(u\)</span>. Some problems may
also register an analytical solution in <tt class="docutils literal"><span class="pre">u_exact</span></tt>. Here is an
example of implementing the logistic ODE from the section <a class="reference internal" href="#ode-sec-exgr"><em>First Example: Logistic Growth</em></a>:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">odespy</span>

<span class="k">class</span> <span class="nc">Logistic</span><span class="p">(</span><span class="n">odespy</span><span class="o">.</span><span class="n">problems</span><span class="o">.</span><span class="n">Problem</span><span class="p">):</span>
    <span class="n">short_description</span> <span class="o">=</span> <span class="s">&quot;Logistic equation&quot;</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">A</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="n">a</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">R</span> <span class="o">=</span> <span class="n">R</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">U0</span> <span class="o">=</span> <span class="n">A</span>

    <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="n">a</span><span class="p">,</span> <span class="n">R</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">R</span>  <span class="c"># short form</span>
        <span class="k">return</span> <span class="n">a</span><span class="o">*</span><span class="n">u</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">u</span><span class="o">/</span><span class="n">R</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">jac</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="n">a</span><span class="p">,</span> <span class="n">R</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">R</span>  <span class="c"># short form</span>
        <span class="k">return</span> <span class="n">a</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">u</span><span class="o">/</span><span class="n">R</span><span class="p">)</span> <span class="o">+</span> <span class="n">a</span><span class="o">*</span><span class="n">u</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="mf">1.</span><span class="o">/</span><span class="n">R</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">u_exact</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="n">a</span><span class="p">,</span> <span class="n">R</span><span class="p">,</span> <span class="n">U0</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">R</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">U0</span>  <span class="c"># short form</span>
        <span class="k">return</span> <span class="n">R</span><span class="o">*</span><span class="n">U0</span><span class="o">*</span><span class="n">numpy</span><span class="o">.</span><span class="n">exp</span><span class="p">(</span><span class="n">a</span><span class="o">*</span><span class="n">t</span><span class="p">)</span><span class="o">/</span><span class="p">(</span><span class="n">R</span> <span class="o">+</span> <span class="n">U0</span><span class="o">*</span><span class="p">(</span><span class="n">numpy</span><span class="o">.</span><span class="n">exp</span><span class="p">(</span><span class="n">a</span><span class="o">*</span><span class="n">t</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span>
</pre></div>
</div>
<p>The <tt class="docutils literal"><span class="pre">stiff</span></tt>, <tt class="docutils literal"><span class="pre">complex_</span></tt>, and <tt class="docutils literal"><span class="pre">not_suitable_solvers</span></tt> class variables
can just be inherited. Note that <tt class="docutils literal"><span class="pre">u_exact</span></tt> should work for a vector <tt class="docutils literal"><span class="pre">t</span></tt>
so <tt class="docutils literal"><span class="pre">numpy</span></tt> versions of mathematical functions must be used.</p>
<p>The initial condition is by convention stored as the attribute <tt class="docutils literal"><span class="pre">U0</span></tt>
in a subclass of <tt class="docutils literal"><span class="pre">Problem</span></tt>, and specified as argument to the constructor.</p>
<p>Here are the typical steps when using such a problem class:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">problem</span> <span class="o">=</span> <span class="n">Logistic</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">R</span><span class="o">=</span><span class="mf">1E+5</span><span class="p">,</span> <span class="n">A</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RK4</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">U0</span><span class="p">)</span>
<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>
</pre></div>
</div>
<p>The problem class may also feature additional methods:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">MyProblem</span><span class="p">(</span><span class="n">odespy</span><span class="o">.</span><span class="n">problems</span><span class="o">.</span><span class="n">Problem</span><span class="p">)</span>
    <span class="o">...</span>
    <span class="k">def</span> <span class="nf">constraints</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Python function for additional constraints: g(u,t)=0.&quot;&quot;&quot;</span>

    <span class="k">def</span> <span class="nf">define_command_line_arguments</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parser</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">        Initialize an argparse object for reading command-line</span>
<span class="sd">        option-value pairs. `parser` is an ``argparse`` object.</span>
<span class="sd">        &quot;&quot;&quot;</span>

    <span class="k">def</span> <span class="nf">verify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">atol</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">rtol</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">        Return True if u at time points t coincides with an exact</span>
<span class="sd">        solution within the prescribed tolerances. If one of the</span>
<span class="sd">        tolerances is None, return max computed error (infinity</span>
<span class="sd">        norm). Return None if the solution cannot be verified.</span>
<span class="sd">        &quot;&quot;&quot;</span>
</pre></div>
</div>
<p>The module <tt class="docutils literal"><span class="pre">odespy.problems</span></tt> contains many predefined ODE problems.</p>
</div>
<div class="section" id="example-solving-a-complex-ode-problem">
<h3>Example: Solving a Complex ODE Problem<a class="headerlink" href="#example-solving-a-complex-ode-problem" title="Permalink to this headline">¶</a></h3>
<p>Many of the solvers offered by Odespy can deal with complex-valued
ODE problems. Consider</p>
<div class="math">
\[u' = iwu,\quad u(0)=1,\]</div>
<p>where <span class="math">\(i=\sqrt{-1}\)</span> is the imaginary unit.
The right-hand side is implemented as <tt class="docutils literal"><span class="pre">1j*w*u</span></tt> in Python since
Python applies <tt class="docutils literal"><span class="pre">j</span></tt> as the imaginary unit in complex numbers.</p>
<div class="section" id="quick-implementation">
<h4>Quick Implementation<a class="headerlink" href="#quick-implementation" title="Permalink to this headline">¶</a></h4>
<p>For complex-valued ODEs, i.e., complex-valued right-hand side functions
or initial conditions, the argument <tt class="docutils literal"><span class="pre">complex_valued=True</span></tt> must be
supplied to the constructor. A complete program reads</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
    <span class="k">return</span> <span class="mi">1j</span><span class="o">*</span><span class="n">w</span><span class="o">*</span><span class="n">u</span>

<span class="kn">import</span> <span class="nn">odespy</span><span class="o">,</span> <span class="nn">numpy</span>

<span class="n">w</span> <span class="o">=</span> <span class="mi">2</span><span class="o">*</span><span class="n">numpy</span><span class="o">.</span><span class="n">pi</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RK4</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">complex_valued</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="mi">0j</span><span class="p">)</span>
<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">numpy</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">101</span><span class="p">))</span>
</pre></div>
</div>
<p>The function <tt class="docutils literal"><span class="pre">odespy.list_not_suitable_complex_solvers()</span></tt>
returns a list of all the classes in Odespy that are not suitable
for complex-valued ODE problems.</p>
</div>
<div class="section" id="comparison-of-methods">
<h4>Comparison of Methods<a class="headerlink" href="#comparison-of-methods" title="Permalink to this headline">¶</a></h4>
<p>We can try three classes that do work for complex-valued ODEs: <tt class="docutils literal"><span class="pre">Vode</span></tt>,
<tt class="docutils literal"><span class="pre">RK4</span></tt>, and <tt class="docutils literal"><span class="pre">RKFehlberg</span></tt>. Comparing these with respect to CPU time and
final error for a very long time integration of 600 periods is carried
out by the following program.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
    <span class="k">return</span> <span class="mi">1j</span><span class="o">*</span><span class="n">w</span><span class="o">*</span><span class="n">u</span>

<span class="kn">import</span> <span class="nn">odespy</span><span class="o">,</span> <span class="nn">numpy</span><span class="o">,</span> <span class="nn">time</span>

<span class="n">w</span> <span class="o">=</span> <span class="mi">2</span><span class="o">*</span><span class="n">numpy</span><span class="o">.</span><span class="n">pi</span>
<span class="n">n</span> <span class="o">=</span> <span class="mi">600</span>  <span class="c"># no of periods</span>
<span class="n">r</span> <span class="o">=</span> <span class="mi">40</span>   <span class="c"># resolution of each period</span>
<span class="n">tp</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">n</span><span class="o">*</span><span class="n">r</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>

<span class="n">solvers</span> <span class="o">=</span> <span class="p">[</span><span class="n">odespy</span><span class="o">.</span><span class="n">Vode</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">complex_valued</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
                       <span class="n">atol</span><span class="o">=</span><span class="mf">1E-7</span><span class="p">,</span> <span class="n">rtol</span><span class="o">=</span><span class="mf">1E-6</span><span class="p">,</span>
                       <span class="n">adams_or_bdf</span><span class="o">=</span><span class="s">&#39;adams&#39;</span><span class="p">),</span>
           <span class="n">odespy</span><span class="o">.</span><span class="n">RK4</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">complex_valued</span><span class="o">=</span><span class="bp">True</span><span class="p">),</span>
           <span class="n">odespy</span><span class="o">.</span><span class="n">RKFehlberg</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">complex_valued</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
                             <span class="n">atol</span><span class="o">=</span><span class="mf">1E-7</span><span class="p">,</span> <span class="n">rtol</span><span class="o">=</span><span class="mf">1E-6</span><span class="p">)]</span>
<span class="n">cpu</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">solver</span> <span class="ow">in</span> <span class="n">solvers</span><span class="p">:</span>
    <span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="mi">0j</span><span class="p">)</span>
    <span class="n">t0</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">clock</span><span class="p">()</span>
    <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">tp</span><span class="p">)</span>
    <span class="n">t1</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">clock</span><span class="p">()</span>
    <span class="n">cpu</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">t1</span><span class="o">-</span><span class="n">t0</span><span class="p">)</span>

<span class="c"># Compare solutions at the end point:</span>
<span class="n">exact</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">exp</span><span class="p">(</span><span class="mi">1j</span><span class="o">*</span><span class="n">w</span><span class="o">*</span><span class="n">tp</span><span class="p">)</span><span class="o">.</span><span class="n">real</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="n">min_cpu</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">cpu</span><span class="p">);</span> <span class="n">cpu</span> <span class="o">=</span> <span class="p">[</span><span class="n">c</span><span class="o">/</span><span class="n">min_cpu</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">cpu</span><span class="p">]</span>  <span class="c"># normalize</span>
<span class="k">print</span> <span class="s">&#39;Exact: u(</span><span class="si">%g</span><span class="s">)=</span><span class="si">%g</span><span class="s">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">tp</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="n">exact</span><span class="p">)</span>
<span class="k">for</span> <span class="n">solver</span><span class="p">,</span> <span class="n">cpu_time</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">solvers</span><span class="p">,</span> <span class="n">cpu</span><span class="p">):</span>
    <span class="k">print</span> <span class="s">&#39;</span><span class="si">%-15s</span><span class="s"> u(</span><span class="si">%g</span><span class="s">)=</span><span class="si">%.6f</span><span class="s"> (error: </span><span class="si">%10.2E</span><span class="s">, cpu: </span><span class="si">%.1f</span><span class="s">)&#39;</span> <span class="o">%</span> \
          <span class="p">(</span><span class="n">solver</span><span class="o">.</span><span class="n">__class__</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span>
           <span class="n">solver</span><span class="o">.</span><span class="n">t</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="n">solver</span><span class="o">.</span><span class="n">u</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">real</span><span class="p">,</span>
           <span class="n">exact</span> <span class="o">-</span> <span class="n">solver</span><span class="o">.</span><span class="n">u</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">real</span><span class="p">,</span> <span class="n">cpu_time</span><span class="p">)</span>
</pre></div>
</div>
<p>We remark that the solution and the corresponding time values can always
be recovered as <tt class="docutils literal"><span class="pre">solver.u</span></tt> and <tt class="docutils literal"><span class="pre">solver.t</span></tt>, respectively.</p>
<p>The output from the program may read</p>
<div class="highlight-text"><div class="highlight"><pre>Exact: u(600)=1
Vode            u(600)=1.001587 (error:  -1.59E-03, cpu: 1.0)
RK4             u(600)=0.997328 (error:   2.67E-03, cpu: 1.3)
RKFehlberg      u(600)=1.000953 (error:  -9.53E-04, cpu: 7.5)
</pre></div>
</div>
<p>The <tt class="docutils literal"><span class="pre">Vode</span></tt> solver is a wrapper of the FORTRAN code <tt class="docutils literal"><span class="pre">zvode.f</span></tt> in
<tt class="docutils literal"><span class="pre">scipy.integrate.ode</span></tt> and is an adaptive Adams method (with default
settings, as used here),
<tt class="docutils literal"><span class="pre">RK4</span></tt> is a compact and straightforward Runge-Kutta method of order 4 in
pure Python with constant step size, and <tt class="docutils literal"><span class="pre">RKFehlberg</span></tt> is a pure Python
implementation of the adaptive Runge-Kutta-Fehlberg method of order
(4,5).  These methods give approximately the same final error, but
with different CPU times. We observe that the very simple <tt class="docutils literal"><span class="pre">RK4</span></tt> solver
in pure Python compares favorably with the much more sophisticated
FORTRAN subroutine <tt class="docutils literal"><span class="pre">zvode</span></tt>.</p>
</div>
</div>
<div class="section" id="avoiding-callbacks-to-python">
<h3>Avoiding Callbacks to Python<a class="headerlink" href="#avoiding-callbacks-to-python" title="Permalink to this headline">¶</a></h3>
<p>The ODE solvers that are implemented in FORTRAN calls, by default,
the user&#8217;s Python implementation of <span class="math">\(f(u,t)\)</span>. Making many calls from
FORTRAN to Python may introduce significant overhead and slow down the
solution process. When the algorithm is implemented in FORTRAN we should
also implement the right-hand side in FORTRAN and call this right-hand
side subroutine directly. Odespy offers this possibility.</p>
<p>The idea is that the user writes a FORTRAN subroutine defining <span class="math">\(f(u,t)\)</span>.
Thereafter, <tt class="docutils literal"><span class="pre">f2py</span></tt> is used to make this subroutine callable from Python.
If we specify the Python interface to this subroutine as an <tt class="docutils literal"><span class="pre">f_f77</span></tt>
argument to the solver&#8217;s constructor, the Odespy class will make sure
that no callbacks to the <span class="math">\(f(u,t)\)</span> definition go via Python.</p>
<div class="section" id="the-logistic-ode">
<h4>The Logistic ODE<a class="headerlink" href="#the-logistic-ode" title="Permalink to this headline">¶</a></h4>
<p>Here is a minimalistic example involving the logistic ODE from
the section <a class="reference internal" href="#ode-sec-exgr"><em>First Example: Logistic Growth</em></a>. The FORTRAN implementation of <span class="math">\(f(u,t)\)</span>
is more complicated than the Python counterpart. The subroutine
has the signature</p>
<div class="highlight-fortran"><div class="highlight"><pre>      <span class="k">subroutine </span><span class="n">f_f77</span><span class="p">(</span><span class="n">neq</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">udot</span><span class="p">)</span>
<span class="n">Cf2py</span> <span class="k">intent</span><span class="p">(</span><span class="n">hide</span><span class="p">)</span> <span class="n">neq</span>
<span class="n">Cf2py</span> <span class="k">intent</span><span class="p">(</span><span class="n">out</span><span class="p">)</span> <span class="n">udot</span>
      <span class="kt">integer </span><span class="n">neq</span>
      <span class="kt">double precision </span><span class="n">t</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">udot</span>
      <span class="k">dimension </span><span class="n">u</span><span class="p">(</span><span class="n">neq</span><span class="p">),</span> <span class="n">udot</span><span class="p">(</span><span class="n">neq</span><span class="p">)</span>
</pre></div>
</div>
<p>This means that there are two additional arguments: <tt class="docutils literal"><span class="pre">neq</span></tt> for the number
of equations in the ODE system, and <tt class="docutils literal"><span class="pre">udot</span></tt> for the array of <span class="math">\(f(u,t)\)</span>
that is output from the subroutine.</p>
<p>We write the FORTRAN implementation of <span class="math">\(f(u,t)\)</span> in a string:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">a</span> <span class="o">=</span> <span class="mi">2</span>
<span class="n">R</span> <span class="o">=</span> <span class="mf">1E+5</span>

<span class="n">f_f77_str</span> <span class="o">=</span> <span class="s">&quot;&quot;&quot;</span>
<span class="s">      subroutine f_f77(neq, t, u, udot)</span>
<span class="s">Cf2py intent(hide) neq</span>
<span class="s">Cf2py intent(out) udot</span>
<span class="s">      integer neq</span>
<span class="s">      double precision t, u, udot</span>
<span class="s">      dimension u(neq), udot(neq)</span>
<span class="s">      udot(1) = </span><span class="si">%.3f</span><span class="s">*u(1)*(1 - u(1)/</span><span class="si">%.1f</span><span class="s">)</span>
<span class="s">      return</span>
<span class="s">      end</span>
<span class="s">&quot;&quot;&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">R</span><span class="p">)</span>
</pre></div>
</div>
<p>Observe that we can transfer problem parameters to the FORTRAN subroutine
by writing their values directly into the FORTRAN source code.
The other alternative would be to transfer the parameters as global
(COMMON block) variables to the FORTRAN code, which is technically
much more complicated.
Also observe that we need to deal with <tt class="docutils literal"><span class="pre">udot</span></tt> and <tt class="docutils literal"><span class="pre">u</span></tt> as arrays even
for a scalar ODE.</p>
<p>Using <tt class="docutils literal"><span class="pre">f2py</span></tt> to compile the string into a Python module is automated
by the <tt class="docutils literal"><span class="pre">odespy.compile_f77</span></tt> function:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">odespy</span>
<span class="n">f_f77</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">compile_f77</span><span class="p">(</span><span class="n">f_f77_str</span><span class="p">)</span>
</pre></div>
</div>
<p>The returned object <tt class="docutils literal"><span class="pre">f_f77</span></tt> is a callable object that allows the
FORTRAN subroutine to be called as <tt class="docutils literal"><span class="pre">udot</span> <span class="pre">=</span> <span class="pre">f_f77(t,</span> <span class="pre">u)</span></tt> from Python.
(However, the Odespy solvers will not use <tt class="docutils literal"><span class="pre">f_f77</span></tt> directly, but rather
its function pointer to the FORTRAN subroutine, and transfer this pointer
to the FORTRAN solver. The switching between <tt class="docutils literal"><span class="pre">t,</span> <span class="pre">u</span></tt> and <tt class="docutils literal"><span class="pre">u,</span> <span class="pre">t</span></tt> arguments
is taken care of. All necessary steps are automatically done behind
the scene.)</p>
<p>The solver can be declared as</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">Lsode</span><span class="p">(</span><span class="n">f</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">f_f77</span><span class="o">=</span><span class="n">f_f77</span><span class="p">)</span>
</pre></div>
</div>
<p>Several solvers accept FORTRAN definitions of the right-hand side:
<tt class="docutils literal"><span class="pre">Lsode</span></tt>, <tt class="docutils literal"><span class="pre">Lsoda</span></tt>, and the other ODEPACK solvers, <tt class="docutils literal"><span class="pre">RKC</span></tt>, <tt class="docutils literal"><span class="pre">RKF45</span></tt>,
<tt class="docutils literal"><span class="pre">Radau5</span></tt>. Look up the documentation of their <tt class="docutils literal"><span class="pre">f_f77</span></tt> parameter to
see exactly what arguments and conventions that the FORTRAN subroutine
demand.</p>
<p>The file <a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/logistic10.py">logistic10.py</a> contains a complete program for solving the logistic ODE
with <span class="math">\(f(u,t)\)</span> implemented in Fortran.</p>
</div>
<div class="section" id="implementing-the-van-der-pol-equation-in-fortran">
<h4>Implementing the van der Pol Equation in FORTRAN<a class="headerlink" href="#implementing-the-van-der-pol-equation-in-fortran" title="Permalink to this headline">¶</a></h4>
<p>As a slightly more complicated example, also involving a subclass
of <tt class="docutils literal"><span class="pre">Problem</span></tt> and computation of the Jacobian and <span class="math">\(f(u,t)\)</span> in
FORTRAN, we consider the van der Pol equation,</p>
<div class="math">
\[y'' = \mu (1 - y^2) y' - y,\quad y(0)=2,\ y'(0)=1,\]</div>
<p>written as a system as shown in the section <em class="xref std std-ref">ode:sec:motivation</em>.
We start by implementing a problem class with Python code for
<span class="math">\(f(u,t)\)</span> and its Jacobian:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">odespy</span>

<span class="k">class</span> <span class="nc">VanDerPolOscillator</span><span class="p">(</span><span class="n">odespy</span><span class="o">.</span><span class="n">problems</span><span class="o">.</span><span class="n">Problem</span><span class="p">):</span>
    <span class="n">short_description</span> <span class="o">=</span> <span class="s">&quot;Van der Pol oscillator&quot;</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">U0</span><span class="o">=</span><span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span> <span class="n">mu</span><span class="o">=</span><span class="mf">3.</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">U0</span> <span class="o">=</span> <span class="n">U0</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">mu</span> <span class="o">=</span> <span class="n">mu</span>

    <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="n">u_0</span><span class="p">,</span> <span class="n">u_1</span> <span class="o">=</span> <span class="n">u</span>
        <span class="n">mu</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">mu</span>
        <span class="k">return</span> <span class="p">[</span><span class="n">u_1</span><span class="p">,</span> <span class="n">mu</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">u_0</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="n">u_1</span> <span class="o">-</span> <span class="n">u_0</span><span class="p">]</span>

    <span class="k">def</span> <span class="nf">jac</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="n">u_0</span><span class="p">,</span> <span class="n">u_1</span> <span class="o">=</span> <span class="n">u</span>
        <span class="n">mu</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">mu</span>
        <span class="k">return</span> <span class="p">[[</span><span class="mf">0.</span><span class="p">,</span> <span class="mf">1.</span><span class="p">],</span>
                <span class="p">[</span><span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">mu</span><span class="o">*</span><span class="n">u_0</span><span class="o">*</span><span class="n">u_1</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">mu</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">u_0</span><span class="o">**</span><span class="mi">2</span><span class="p">)]]</span>
</pre></div>
</div>
<p>Now, we want to provide <tt class="docutils literal"><span class="pre">f</span></tt> and <tt class="docutils literal"><span class="pre">jac</span></tt> in FORTRAN as well.
The FORTRAN code for <span class="math">\(f(u,t)\)</span> can be returned from a method:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">VanDerPolOscillator</span><span class="p">(</span><span class="n">odespy</span><span class="o">.</span><span class="n">problems</span><span class="o">.</span><span class="n">Problem</span><span class="p">):</span>
    <span class="o">...</span>
    <span class="k">def</span> <span class="nf">str_f_f77</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Return f(u,t) as Fortran source code string.&quot;&quot;&quot;</span>
        <span class="k">return</span> <span class="s">&quot;&quot;&quot;</span>
<span class="s">      subroutine f_f77(neq, t, u, udot)</span>
<span class="s">Cf2py intent(hide) neq</span>
<span class="s">Cf2py intent(out) udot</span>
<span class="s">      integer neq</span>
<span class="s">      double precision t, u, udot</span>
<span class="s">      dimension u(neq), udot(neq)</span>
<span class="s">      udot(1) = u(2)</span>
<span class="s">      udot(2) = </span><span class="si">%g</span><span class="s">*(1 - u(1)**2)*u(2) - u(1)</span>
<span class="s">      return</span>
<span class="s">      end</span>
<span class="s">&quot;&quot;&quot;</span> <span class="o">%</span> <span class="bp">self</span><span class="o">.</span><span class="n">mu</span>
</pre></div>
</div>
<p>While all FORTRAN solvers supported by Odespy so far employ the same
signature for the <span class="math">\(f(u,t)\)</span> function, different solvers apply different
signatures for the Jacobian. Here are two versions for ODEPACK and
<tt class="docutils literal"><span class="pre">Radau5</span></tt>, respectively:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">VanDerPolOscillator</span><span class="p">(</span><span class="n">odespy</span><span class="o">.</span><span class="n">problems</span><span class="o">.</span><span class="n">Problem</span><span class="p">):</span>
    <span class="o">...</span>
    <span class="k">def</span> <span class="nf">str_jac_f77_fadau5</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">&quot;&quot;&quot;</span>
<span class="s">      subroutine jac_f77_radau5(neq,t,u,dfu,ldfu,rpar,ipar)</span>
<span class="s">Cf2py intent(hide) neq,rpar,ipar</span>
<span class="s">Cf2py intent(in)   t,u,ldfu</span>
<span class="s">Cf2py intent(out) dfu</span>
<span class="s">      integer neq,ipar,ldfu</span>
<span class="s">      double precision t,u,dfu,rpar</span>
<span class="s">      dimension u(neq),dfu(ldfu,neq),rpar(*),ipar(*)</span>
<span class="s">      dfu(1,1) = 0</span>
<span class="s">      dfu(1,2) = 1</span>
<span class="s">      dfu(2,1) = -2*</span><span class="si">%g</span><span class="s">*u(1)*u(2) - 1</span>
<span class="s">      dfu(2,2) = </span><span class="si">%g</span><span class="s">*(1-u(1)**2)</span>
<span class="s">      return</span>
<span class="s">      end</span>
<span class="s">&quot;&quot;&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mu</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">mu</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">str_jac_f77_lsode_dense</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">&quot;&quot;&quot;</span>
<span class="s">      subroutine jac_f77(neq, t, u, ml, mu, pd, nrowpd)</span>
<span class="s">Cf2py intent(hide) neq, ml, mu, nrowpd</span>
<span class="s">Cf2py intent(out) pd</span>
<span class="s">      integer neq, ml, mu, nrowpd</span>
<span class="s">      double precision t, u, pd</span>
<span class="s">      dimension u(neq), pd(nrowpd,neq)</span>
<span class="s">      pd(1,1) = 0</span>
<span class="s">      pd(1,2) = 1</span>
<span class="s">      pd(2,1) = -2*</span><span class="si">%g</span><span class="s">*u(1)*u(2) - 1</span>
<span class="s">      pd(2,2) = </span><span class="si">%g</span><span class="s">*(1 - u(1)**2)</span>
<span class="s">      return</span>
<span class="s">      end</span>
<span class="s">&quot;&quot;&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mu</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">mu</span><span class="p">)</span>
</pre></div>
</div>
<p>For the <tt class="docutils literal"><span class="pre">Lsode</span></tt> solver we can also provide the Jacobian in banded matrix
format (this is not yet supported for <tt class="docutils literal"><span class="pre">Radau5</span></tt>, but the underlying
FORTRAN code allows a banded Jacobian).</p>
<p>Having some methods returning FORTRAN code, we need to turn
the source code into Python modules. This is done by
<tt class="docutils literal"><span class="pre">odespy.compile_f77</span></tt> in the constructor of class <tt class="docutils literal"><span class="pre">VanDerPolOscillator</span></tt>:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">VanDerPolOscillator</span><span class="p">(</span><span class="n">odespy</span><span class="o">.</span><span class="n">problems</span><span class="o">.</span><span class="n">Problem</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">U0</span><span class="o">=</span><span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span> <span class="n">mu</span><span class="o">=</span><span class="mf">3.</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">U0</span> <span class="o">=</span> <span class="n">U0</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">mu</span> <span class="o">=</span> <span class="n">mu</span>

        <span class="c"># Compile F77 functions</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">f_f77</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">jac_f77_radau5</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">jac_f77_lsode</span> <span class="o">=</span> \
                    <span class="n">compile_f77</span><span class="p">([</span><span class="bp">self</span><span class="o">.</span><span class="n">str_f_f77</span><span class="p">(),</span>
                                 <span class="bp">self</span><span class="o">.</span><span class="n">str_jac_f77_radau5</span><span class="p">(),</span>
                                 <span class="bp">self</span><span class="o">.</span><span class="n">str_jac_f77_lsode</span><span class="p">()])</span>
</pre></div>
</div>
<p>The application of this problem class goes as follows:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">problem</span> <span class="o">=</span> <span class="n">VanDerPolOscillator</span><span class="p">()</span>
<span class="kn">import</span> <span class="nn">odespy</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">Radau5</span><span class="p">(</span><span class="n">f</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">f</span><span class="o">=</span><span class="n">problem</span><span class="o">.</span><span class="n">f_f77</span><span class="p">,</span>
                       <span class="n">jac</span><span class="o">=</span><span class="n">problem</span><span class="o">.</span><span class="n">jac_f77_radau5</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">U0</span><span class="p">)</span>
<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>
<div class="section" id="example-solving-a-stochastic-differential-equation">
<h3>Example: Solving a Stochastic Differential Equation<a class="headerlink" href="#example-solving-a-stochastic-differential-equation" title="Permalink to this headline">¶</a></h3>
<p>We consider an oscillator driven by stochastic white noise:</p>
<div class="math">
\[x''(t) + bx'(t) + cx(t) = N(t),\ x(0)=X,\ x'(0) =0,\]</div>
<p>where <span class="math">\(N(t)\)</span> is the white noise function computed numerically as</p>
<div class="math">
\[N(t_i) \approx \sigma\frac{\Delta W_i}{\sqrt{t_{i+1}-t_i}},\]</div>
<p>where <span class="math">\(\Delta W_1,\Delta W_2,\ldots\)</span> are independent normally
distributed random variables with mean zero and unit standard
deviation, and <span class="math">\(\sigma\)</span> is the strength of the noise.  The physical
feature of this problem is that <span class="math">\(N(t)\)</span> provides an excitation
containing &#8220;all&#8221; frequencies, but the oscillator is a strong filter:
with low damping one of the frequencies in <span class="math">\(N(t)\)</span> will hit the
resonance frequency <span class="math">\(\sqrt{c}/(2\pi)\)</span> which will then dominate the
output signal <span class="math">\(x(t)\)</span>.</p>
<p>The noise is additive in this stochastic differential equation so
there is no difference between the Ito and Stratonovich interpretations
of the equation.</p>
<p>The challenge with this model problem is that stochastic differential
equations do not fit with the user interface offered by Odespy,
since the right-hand side function is assumed to be dependent only
on the solution and the present time (<tt class="docutils literal"><span class="pre">f(u,t)</span></tt>), and additional
user-defined parameters, but for the present problem the right-hand
side function needs information about <span class="math">\(N(t)\)</span> and hence
the size of the current time step.</p>
<p>We can solve this issue by having a reference to the solver in the
right-hand side function, precomputing <span class="math">\(N(t_i)\)</span> for all time intervals
<span class="math">\(i\)</span>, and using the <tt class="docutils literal"><span class="pre">n</span></tt> attribute in the solver for selecting the right
force term (recall that some methods will call the right-hand side
function many times during a time interval - all these calls must use
the same value of the white noise).</p>
<p>The right-hand side function must do many things so a class is
appropriate:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">WhiteNoiseOscillator</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="n">sigma</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">b</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">c</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">sigma</span> <span class="o">=</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="n">sigma</span>

    <span class="k">def</span> <span class="nf">connect_solver</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">solver</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;Solver is needed for time step number and size.&quot;&quot;&quot;</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">solver</span> <span class="o">=</span> <span class="n">solver</span>

    <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s">&#39;N&#39;</span><span class="p">):</span>  <span class="c"># is self.N not yet computed?</span>
            <span class="c"># Compute N(t) for all time intervals</span>
            <span class="kn">import</span> <span class="nn">numpy</span>
            <span class="n">numpy</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span>
            <span class="n">t</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">solver</span><span class="o">.</span><span class="n">t</span>
            <span class="n">dW</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">normal</span><span class="p">(</span><span class="n">loc</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="nb">len</span><span class="p">(</span><span class="n">t</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
            <span class="n">dt</span> <span class="o">=</span> <span class="n">t</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span> <span class="o">-</span> <span class="n">t</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">N</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">sigma</span><span class="o">*</span><span class="n">dW</span><span class="o">/</span><span class="n">numpy</span><span class="o">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">dt</span><span class="p">)</span>

        <span class="n">x</span><span class="p">,</span> <span class="n">v</span> <span class="o">=</span> <span class="n">u</span>
        <span class="n">N</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">N</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">solver</span><span class="o">.</span><span class="n">n</span><span class="p">]</span>
        <span class="k">return</span> <span class="p">[</span><span class="n">v</span><span class="p">,</span> <span class="n">N</span> <span class="o">-</span><span class="bp">self</span><span class="o">.</span><span class="n">b</span><span class="o">*</span><span class="n">v</span> <span class="o">-</span><span class="bp">self</span><span class="o">.</span><span class="n">c</span><span class="o">*</span><span class="n">x</span><span class="p">]</span>
</pre></div>
</div>
<p>Note that <span class="math">\(N(t)\)</span> is computed on demand the first time the right-hand side
function is called. We need to wait until the <tt class="docutils literal"><span class="pre">f</span></tt> method is called since
we need access to the solver instance to compute the <tt class="docutils literal"><span class="pre">self.N</span></tt> array.</p>
<p>It is easy to compare different methods for solving this stochastic equation:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">problem</span> <span class="o">=</span> <span class="n">WhiteNoiseOscillator</span><span class="p">(</span><span class="n">b</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span> <span class="n">c</span><span class="o">=</span><span class="n">pi</span><span class="o">**</span><span class="mi">2</span><span class="p">,</span> <span class="n">sigma</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">solvers</span> <span class="o">=</span> <span class="p">[</span><span class="n">odespy</span><span class="o">.</span><span class="n">Heun</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">),</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RK4</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">),</span>
           <span class="n">odespy</span><span class="o">.</span><span class="n">ForwardEuler</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">)]</span>
<span class="k">for</span> <span class="n">solver</span> <span class="ow">in</span> <span class="n">solvers</span><span class="p">:</span>
    <span class="n">f</span><span class="o">.</span><span class="n">connect_solver</span><span class="p">(</span><span class="n">solver</span><span class="p">)</span>
    <span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">])</span>  <span class="c"># start from rest</span>
    <span class="n">T</span> <span class="o">=</span> <span class="mi">60</span>   <span class="c"># with c=pi**2, the period is 1</span>
    <span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="mi">10001</span><span class="p">))</span>

    <span class="n">x</span> <span class="o">=</span> <span class="n">u</span><span class="p">[:,</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span>
    <span class="n">hold</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span>

<span class="n">legend</span><span class="p">([</span><span class="nb">str</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">solvers</span><span class="p">])</span>
</pre></div>
</div>
<p>All solutions are also stored in the solver objects
as attributes <tt class="docutils literal"><span class="pre">u</span></tt> and <tt class="docutils literal"><span class="pre">t</span></tt>, so we
may easily extract the solution of <tt class="docutils literal"><span class="pre">RK4</span></tt> by</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">solvers</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">u</span><span class="p">,</span> <span class="n">solvers</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">t</span>
</pre></div>
</div>
<div class="figure">
<a class="reference internal image-reference" href="_images/sode1.png"><img alt="_images/sode1.png" src="_images/sode1.png" style="width: 500px;" /></a>
<p class="caption">Oscillator driven by white noise</p>
</div>
<p>The <tt class="docutils literal"><span class="pre">Heun</span></tt> and <tt class="docutils literal"><span class="pre">RK2</span></tt> methods give coinciding solutions while
the <tt class="docutils literal"><span class="pre">ForwardEuler</span></tt> method gives too large amplitudes.
The frequency is 0.5 (period 2) as expected.</p>
<p>In this example the white noise force is computed only once since
the <tt class="docutils literal"><span class="pre">f</span></tt> instance is reused in all methods. If a new <tt class="docutils literal"><span class="pre">f</span></tt> is created
for each method, it is crucial that the same seed of the random
generator is used for all methods, so that the time evolution of
the force is always the same - otherwise the solutions will be
different.</p>
<p>The complete code is available in <a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/tutorial/src-odespy/sode1.py">sode.py</a>.</p>
</div>
</div>
<div class="section" id="adaptive-methods">
<h2>Adaptive Methods<a class="headerlink" href="#adaptive-methods" title="Permalink to this headline">¶</a></h2>
<p>The solvers used in the previous examples have all employed a constant
time step <span class="math">\(\Delta t\)</span>. Many solvers available through the Odespy
interface are adaptive in the sense that <span class="math">\(\Delta t\)</span> is adjusted
throughout the solution process to meet a prescribed tolerance for
the estimated error.</p>
<p>Simple methods such as <tt class="docutils literal"><span class="pre">RK4</span></tt> apply time steps</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">dt</span> <span class="o">=</span> <span class="n">time_points</span><span class="p">[</span><span class="n">k</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">time_points</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
</pre></div>
</div>
<p>while adaptive methods will use several (smaller) time steps than <tt class="docutils literal"><span class="pre">dt</span></tt>
in each <tt class="docutils literal"><span class="pre">dt</span></tt> interval to ensure that the estimated numerical error is
smaller than some prescribed tolerance. The estimated numerical error
may be a rather crude quantitative measure of the true numerical
error (which we do not know since the exact solution of the problem is
in general not known).</p>
<p>Some adaptive solvers record the intermediate solutions in each <tt class="docutils literal"><span class="pre">dt</span></tt>
interval in arrays <tt class="docutils literal"><span class="pre">self.u_all</span></tt> and <tt class="docutils literal"><span class="pre">self.t_all</span></tt>.  Examples include
<tt class="docutils literal"><span class="pre">RKFehlberg</span></tt>, <tt class="docutils literal"><span class="pre">Fehlberg</span></tt>, <tt class="docutils literal"><span class="pre">DormandPrince</span></tt>, <tt class="docutils literal"><span class="pre">CashKarp</span></tt>, and
<tt class="docutils literal"><span class="pre">BogackiShampine</span></tt>. Other adaptive solvers (<tt class="docutils literal"><span class="pre">Vode</span></tt>, <tt class="docutils literal"><span class="pre">Lsode</span></tt>, <tt class="docutils literal"><span class="pre">Lsoda</span></tt>,
<tt class="docutils literal"><span class="pre">RKC</span></tt>, <tt class="docutils literal"><span class="pre">RKF45</span></tt>, etc.)  do not give access to intermediate solution
steps between the user-given time points, specified in the
<tt class="docutils literal"><span class="pre">solver.solve</span></tt> call, and then we only have access to the solution at
the user-given time points as returned by this call. One can run <tt class="docutils literal"><span class="pre">if</span>
<span class="pre">solver.has_u_t_all()</span></tt> to test if the <tt class="docutils literal"><span class="pre">solver.u_all</span></tt> and <tt class="docutils literal"><span class="pre">solver.t_all</span></tt>
arrays are available.  These are of interest to see how the adaptive
strategy works between the user-specified time points.</p>
<div class="section" id="the-test-problem">
<h3>The Test Problem<a class="headerlink" href="#the-test-problem" title="Permalink to this headline">¶</a></h3>
<p>We consider the ODE problem for testing adaptive solvers:</p>
<div class="math" id="equation-gaussian:ode:eq">
<span class="eqno">(1)</span>\[     u' = -\frac{t-c}{s^2} (u-1)\]</div>
<p>The exact solution is a Gaussian function,</p>
<div class="math">
\[u(t) = 1 + \exp{\left(-\frac{1}{2}\left(\frac{t-c}{s}\right)^2\right)}\]</div>
<p>centered around <span class="math">\(t=c\)</span> and width characteristic width (&#8220;standard
deviation&#8221;) <span class="math">\(s\)</span>. The initial condition is taken as the exact <span class="math">\(u\)</span> at <span class="math">\(t=0\)</span>.</p>
<p>Since the Gaussian function is significantly different from zero only in the
interval <span class="math">\([c-3s, c+3s]\)</span>, one may expect that adaptive methods will
efficiently take larger steps when <span class="math">\(u\)</span> is almost constant and increase
the resolution when <span class="math">\(u\)</span> changes substantially in the vicinity
of <span class="math">\(t=c\)</span>. We can test if this is the case with several solvers.</p>
</div>
<div class="section" id="running-simple-methods">
<h3>Running Simple Methods<a class="headerlink" href="#running-simple-methods" title="Permalink to this headline">¶</a></h3>
<p>Let us first use a simple standard method like the 2nd- and 4th-order
Runge-Kutta methods with constant step size. With the former method
(<tt class="docutils literal"><span class="pre">RK2</span></tt>), <span class="math">\(c=3\)</span>, <span class="math">\(s=0.5\)</span>, and <span class="math">\(41\)</span> uniformly distributed points,
the discrepancy between the numerical and exact solution in
Figure <a class="reference internal" href="#gaussian-fig-rk2-41"><em>2nd-order Runge-Kutta method with 41 points</em></a> is substantial. Increasing the number
of points by a factor of 10 gives a solution much closer to the
exact one, and switching to the 4th-order method (<tt class="docutils literal"><span class="pre">RK4</span></tt>) makes the
curves visually coincide. The problem is therefore quite straightforward
to solve using a sufficient number of points (400) and a higher-order method
such as <tt class="docutils literal"><span class="pre">RK4</span></tt>.
For curiosity we can mention that the <tt class="docutils literal"><span class="pre">ForwardEuler</span></tt> method produces
a maximum value of 0.98 with 20,000 points and 0.998 with 200,000 points.</p>
<div class="figure" id="gaussian-fig-rk2-41">
<a class="reference internal image-reference" href="_images/gaussian1_RK2_41.png"><img alt="_images/gaussian1_RK2_41.png" src="_images/gaussian1_RK2_41.png" style="width: 500px;" /></a>
<p class="caption"><em>2nd-order Runge-Kutta method with 41 points</em></p>
</div>
<p>A simple program testing one numerical method goes as follows (<a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/gaussian1.py">gaussian1.py</a>).</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">odespy</span><span class="o">,</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span><span class="o">,</span> <span class="nn">matplotlib.pyplot</span> <span class="kn">as</span> <span class="nn">plt</span>

<span class="n">center_point</span> <span class="o">=</span> <span class="mi">3</span>
<span class="n">s</span> <span class="o">=</span> <span class="mf">0.5</span>

<span class="n">problem</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">problems</span><span class="o">.</span><span class="n">Gaussian1</span><span class="p">(</span><span class="n">c</span><span class="o">=</span><span class="n">center_point</span><span class="p">,</span> <span class="n">s</span><span class="o">=</span><span class="n">s</span><span class="p">)</span>

<span class="n">npoints</span> <span class="o">=</span> <span class="mi">41</span>
<span class="n">tp</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">center_point</span><span class="p">,</span> <span class="n">npoints</span><span class="p">)</span>

<span class="n">method</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RK2</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">method</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">U0</span><span class="p">)</span>

<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">tp</span><span class="p">)</span>

<span class="n">method</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">__class__</span><span class="o">.</span><span class="n">__name__</span>
<span class="k">print</span> <span class="s">&#39;</span><span class="si">%.4f</span><span class="s">  </span><span class="si">%s</span><span class="s">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">u</span><span class="o">.</span><span class="n">max</span><span class="p">(),</span> <span class="n">method</span><span class="p">)</span>

<span class="k">if</span> <span class="n">solver</span><span class="o">.</span><span class="n">has_u_t_all</span><span class="p">():</span>
    <span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">solver</span><span class="o">.</span><span class="n">t_all</span><span class="p">,</span> <span class="n">solver</span><span class="o">.</span><span class="n">u_all</span><span class="p">,</span> <span class="s">&#39;bo&#39;</span><span class="p">,</span>
             <span class="n">tp</span><span class="p">,</span> <span class="n">problem</span><span class="o">.</span><span class="n">u_exact</span><span class="p">(</span><span class="n">tp</span><span class="p">))</span>
    <span class="k">print</span> <span class="s">&#39;</span><span class="si">%s</span><span class="s"> used </span><span class="si">%d</span><span class="s"> steps (</span><span class="si">%d</span><span class="s"> specified)&#39;</span> <span class="o">%</span> \
          <span class="p">(</span><span class="n">method</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">solver</span><span class="o">.</span><span class="n">u_all</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">tp</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
    <span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">tp</span><span class="p">,</span> <span class="n">solver</span><span class="o">.</span><span class="n">u</span><span class="p">,</span> <span class="n">tp</span><span class="p">,</span> <span class="n">problem</span><span class="o">.</span><span class="n">u_exact</span><span class="p">(</span><span class="n">tp</span><span class="p">))</span>
<span class="n">plt</span><span class="o">.</span><span class="n">legend</span><span class="p">([</span><span class="n">method</span><span class="p">,</span> <span class="s">&#39;exact&#39;</span><span class="p">])</span>
<span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmppng&#39;</span><span class="p">);</span> <span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp.pdf&#39;</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">show</span><span class="p">()</span>
</pre></div>
</div>
</div>
<div class="section" id="running-the-runge-kutta-fehlberg-method">
<h3>Running the Runge-Kutta-Fehlberg Method<a class="headerlink" href="#running-the-runge-kutta-fehlberg-method" title="Permalink to this headline">¶</a></h3>
<p>One of the most widely used general-purpose, adaptive methods for
ODE problems is the
Runge-Kutta-Fehlberg method of order (4,5). This method is available
in three alternative implementations in Odespy: a direct Python
version (<tt class="docutils literal"><span class="pre">RKFehlberg</span></tt>), a specialization of a generalized
implementation of explicit adaptive Runge-Kutta methods
(<tt class="docutils literal"><span class="pre">Fehlberg</span></tt>), and as a FORTRAN code (<tt class="docutils literal"><span class="pre">RKF45</span></tt>). We can try one of
these,</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">method</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">Fehlberg</span>
</pre></div>
</div>
<p>Figure <a class="reference internal" href="#gaussian-fig-fehlberg-41"><em>Adaptive Runge-Kutta-Fehlberg method with 57 points (starting with 41)</em></a> shows how <tt class="docutils literal"><span class="pre">Fehlberg</span></tt> with 40 intervals produces a solution of reasonable accuracy. The dots show the actual computational points used by the algorithm (57 adaptively selected points in time).</p>
<div class="figure" id="gaussian-fig-fehlberg-41">
<a class="reference internal image-reference" href="_images/gaussian1_Fehlberg_41.png"><img alt="_images/gaussian1_Fehlberg_41.png" src="_images/gaussian1_Fehlberg_41.png" style="width: 500px;" /></a>
<p class="caption"><em>Adaptive Runge-Kutta-Fehlberg method with 57 points (starting with 41)</em></p>
</div>
<p>Adaptive algorithms apply an error estimate based on considering a higher-order method as exact, in this case a method of order 5, and a method of lower order (here 4) as the numerically predicted solution. The user can specify an error tolerance. In the program above we just relied to the default tolerance, which can be printed by</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">print</span> <span class="n">solver</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
</pre></div>
</div>
<p>yielding a list of all optional parameters:</p>
<div class="highlight-text"><div class="highlight"><pre>{&#39;f_kwargs&#39;: {}, &#39;f_args&#39;: (),
 &#39;max_step&#39;: 1.5000000000000036, &#39;verbose&#39;: 0,
 &#39;min_step&#39;: 0.0014999999999999946,
 &#39;first_step&#39;: 0.14999999999999999,
 &#39;rtol&#39;: 1e-06, &#39;atol&#39;: 1e-08,
 &#39;name of f&#39;: &#39;f&#39;, &#39;complex_valued&#39;: False,
 &#39;disk_storage&#39;: False, &#39;u_exact&#39;: None}
</pre></div>
</div>
<p>The tolerances involved are of relative and absolute type, i.e.,</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">estimated_error</span> <span class="o">&lt;=</span> <span class="n">tol</span> <span class="o">=</span> <span class="n">rtol</span><span class="o">*</span><span class="nb">abs</span><span class="p">(</span><span class="n">u</span><span class="p">)</span> <span class="o">+</span> <span class="n">atol</span>
</pre></div>
</div>
<p>is the typical test on whether the solution is sufficiently.
For very small <tt class="docutils literal"><span class="pre">u</span></tt>, <tt class="docutils literal"><span class="pre">atol</span></tt> comes into play, while for large <tt class="docutils literal"><span class="pre">u</span></tt>, the
relative tolerance <tt class="docutils literal"><span class="pre">rtol</span></tt> dominates.</p>
<p>In this particular example, running <tt class="docutils literal"><span class="pre">RK4</span></tt> with 57 equally spaced points
yields a maximum value of 1.95, while <tt class="docutils literal"><span class="pre">Fehlberg</span></tt> with 57 adaptively
selected points results in 1.98. Note that the tolerances used are
<span class="math">\(10^{-6}\)</span> while the real error is of the order <span class="math">\(10^{-2}\)</span>.</p>
<p>We can specify stricter tolerances and also control the minimum
allowed step size, <tt class="docutils literal"><span class="pre">min_step</span></tt>, which might be too large to achieve
the desired error level (<a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/gaussian2.py">gaussian2.py</a>):</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">rtol</span> <span class="o">=</span> <span class="mf">1E-12</span>
<span class="n">atol</span> <span class="o">=</span> <span class="n">rtol</span>
<span class="n">min_step</span> <span class="o">=</span> <span class="mf">0.000001</span>

<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">Fehlberg</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">,</span> <span class="n">atol</span><span class="o">=</span><span class="n">atol</span><span class="p">,</span> <span class="n">rtol</span><span class="o">=</span><span class="n">rtol</span><span class="p">,</span>
                         <span class="n">min_step</span><span class="o">=</span><span class="n">min_step</span><span class="p">)</span>
</pre></div>
</div>
<p>The <tt class="docutils literal"><span class="pre">Fehlberg</span></tt> solver now applies 701 points and achieves a maximum
value of 2.00005. However, <tt class="docutils literal"><span class="pre">RK4</span></tt> with the same number of (equally spaced) points
achieves the same accuracy and is much faster.</p>
</div>
<div class="section" id="testing-more-adaptive-solvers">
<h3>Testing More Adaptive Solvers<a class="headerlink" href="#testing-more-adaptive-solvers" title="Permalink to this headline">¶</a></h3>
<p>We have already solved <a href="#equation-gaussian:ode:eq">(1)</a> with sufficient accuracy, but
let us see how other methods perform, as this will most likely result in
a surprise. Below is a program (<a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/gaussian3.py">gaussian3.py</a>) that compares several famous and
widely used methods in the same plot.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">odespy</span><span class="o">,</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span><span class="o">,</span> <span class="nn">matplotlib.pyplot</span> <span class="kn">as</span> <span class="nn">plt</span>

<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">problem</span><span class="p">,</span> <span class="n">tp</span><span class="p">,</span> <span class="n">solver</span><span class="p">):</span>
    <span class="n">method</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">__class__</span><span class="o">.</span><span class="n">__name__</span>

    <span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">problem</span><span class="o">.</span><span class="n">U0</span><span class="p">)</span>

    <span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">tp</span><span class="p">)</span>
    <span class="n">solver</span><span class="o">.</span><span class="n">u_max</span> <span class="o">=</span> <span class="n">u</span><span class="o">.</span><span class="n">max</span><span class="p">()</span>
    <span class="k">print</span> <span class="s">&#39;</span><span class="si">%.4f</span><span class="s">  </span><span class="si">%s</span><span class="s">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">solver</span><span class="o">.</span><span class="n">u_max</span><span class="p">,</span> <span class="n">method</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">solver</span><span class="o">.</span><span class="n">has_u_t_all</span><span class="p">():</span>
        <span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">solver</span><span class="o">.</span><span class="n">t_all</span><span class="p">,</span> <span class="n">solver</span><span class="o">.</span><span class="n">u_all</span><span class="p">)</span>
        <span class="k">print</span> <span class="s">&#39;</span><span class="si">%s</span><span class="s"> used </span><span class="si">%d</span><span class="s"> steps (</span><span class="si">%d</span><span class="s"> specified)&#39;</span> <span class="o">%</span> \
              <span class="p">(</span><span class="n">method</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">solver</span><span class="o">.</span><span class="n">u_all</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">tp</span><span class="p">))</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">solver</span><span class="o">.</span><span class="n">t</span><span class="p">,</span> <span class="n">solver</span><span class="o">.</span><span class="n">u</span><span class="p">)</span>
    <span class="n">legend</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">method</span><span class="p">)</span>
    <span class="n">plt</span><span class="o">.</span><span class="n">hold</span><span class="p">(</span><span class="s">&#39;on&#39;</span><span class="p">)</span>

<span class="n">rtol</span> <span class="o">=</span> <span class="mf">1E-6</span>
<span class="n">atol</span> <span class="o">=</span> <span class="n">rtol</span>
<span class="n">s</span> <span class="o">=</span> <span class="mf">0.5</span>
<span class="n">npoints</span> <span class="o">=</span> <span class="mi">41</span>
<span class="n">center_point</span> <span class="o">=</span> <span class="mi">3</span>
<span class="n">problem</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">problems</span><span class="o">.</span><span class="n">Gaussian1</span><span class="p">(</span><span class="n">c</span><span class="o">=</span><span class="n">center_point</span><span class="p">,</span> <span class="n">s</span><span class="o">=</span><span class="n">s</span><span class="p">)</span>
<span class="n">tp</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">center_point</span><span class="p">,</span> <span class="n">npoints</span><span class="p">)</span>
<span class="n">min_step</span> <span class="o">=</span> <span class="mf">0.0001</span>

<span class="n">methods</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;DormandPrince&#39;</span><span class="p">,</span> <span class="s">&#39;BogackiShampine&#39;</span><span class="p">,</span>
           <span class="s">&#39;RKFehlberg&#39;</span><span class="p">,</span> <span class="s">&#39;Vode&#39;</span><span class="p">,</span> <span class="s">&#39;RKF45&#39;</span><span class="p">,</span> <span class="s">&#39;Lsoda&#39;</span><span class="p">]</span>
<span class="n">solvers</span> <span class="o">=</span> <span class="p">[</span><span class="nb">eval</span><span class="p">(</span><span class="s">&#39;odespy.&#39;</span> <span class="o">+</span> <span class="n">method</span><span class="p">)(</span>
           <span class="n">problem</span><span class="o">.</span><span class="n">f</span><span class="p">,</span> <span class="n">atol</span><span class="o">=</span><span class="n">atol</span><span class="p">,</span> <span class="n">rtol</span><span class="o">=</span><span class="n">rtol</span><span class="p">,</span>
           <span class="n">min_step</span><span class="o">=</span><span class="n">min_step</span><span class="p">)</span>
           <span class="k">for</span> <span class="n">method</span> <span class="ow">in</span> <span class="n">methods</span><span class="p">]</span>
<span class="c"># Run Vode with implicit BDF method of order 5</span>
<span class="n">solvers</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">adams_or_bdf</span><span class="o">=</span><span class="s">&#39;bdf&#39;</span><span class="p">,</span> <span class="n">order</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">jac</span><span class="o">=</span><span class="n">problem</span><span class="o">.</span><span class="n">jac</span><span class="p">)</span>

<span class="n">legend</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">solver</span> <span class="ow">in</span> <span class="n">solvers</span><span class="p">:</span>
    <span class="n">run</span><span class="p">(</span><span class="n">problem</span><span class="p">,</span> <span class="n">tp</span><span class="p">,</span> <span class="n">solver</span><span class="p">)</span>

<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">tp</span><span class="p">,</span> <span class="n">problem</span><span class="o">.</span><span class="n">u_exact</span><span class="p">(</span><span class="n">tp</span><span class="p">))</span>
<span class="n">legend</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s">&#39;exact&#39;</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">legend</span><span class="p">(</span><span class="n">legend</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp1.png&#39;</span><span class="p">)</span>

<span class="c"># Plot errors</span>
<span class="n">plt</span><span class="o">.</span><span class="n">figure</span><span class="p">()</span>
<span class="n">exact</span> <span class="o">=</span> <span class="n">problem</span><span class="o">.</span><span class="n">u_exact</span><span class="p">(</span><span class="n">tp</span><span class="p">)</span>
<span class="k">for</span> <span class="n">solver</span> <span class="ow">in</span> <span class="n">solvers</span><span class="p">:</span>
    <span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">tp</span><span class="p">,</span> <span class="n">exact</span> <span class="o">-</span> <span class="n">solver</span><span class="o">.</span><span class="n">u</span><span class="p">)</span>
    <span class="n">plt</span><span class="o">.</span><span class="n">hold</span><span class="p">(</span><span class="s">&#39;on&#39;</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">legend</span><span class="p">(</span><span class="n">legend</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp2.png&#39;</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">show</span><span class="p">()</span>
</pre></div>
</div>
<p>The default discretization applies <span class="math">\(N=40\)</span> equal-sized time intervals,
but adaptive methods should be able to adjust themselves to the
desired error level <span class="math">\(10^{-6}\)</span>. Figures <a class="reference internal" href="#gaussian3-fig1-41"><em>Comparison of adaptive methods with default parameters (tolerance ``E-6``)</em></a> and
<a class="reference internal" href="#gaussian3-fig1err-41"><em>Comparison of errors in adaptive methods with default parameters (tolerance ``E-6``)</em></a> show that this expected behavior is not the
case. There is substantial discrepancy between the methods!
Surprisingly, the well-known FORTRAN codes accessed by the <tt class="docutils literal"><span class="pre">Vode</span></tt>
(<tt class="docutils literal"><span class="pre">vode.f</span></tt>) and <tt class="docutils literal"><span class="pre">Lsoda</span></tt> (from ODEPACK) methods give very inaccurate
results, despite setting <tt class="docutils literal"><span class="pre">Vode</span></tt> to use a stiff BDF solver of order 5, and
<tt class="docutils literal"><span class="pre">Lsoda</span></tt> should automatically select between nonstiff and stiff solvers
(of default order 4 in this case).</p>
<div class="figure" id="gaussian3-fig1-41">
<a class="reference internal image-reference" href="_images/gaussian3_41.png"><img alt="_images/gaussian3_41.png" src="_images/gaussian3_41.png" style="width: 500px;" /></a>
<p class="caption"><em>Comparison of adaptive methods with default parameters (tolerance ``E-6``)</em></p>
</div>
<div class="figure" id="gaussian3-fig1err-41">
<a class="reference internal image-reference" href="_images/gaussian3e_41.png"><img alt="_images/gaussian3e_41.png" src="_images/gaussian3e_41.png" style="width: 500px;" /></a>
<p class="caption"><em>Comparison of errors in adaptive methods with default parameters (tolerance ``E-6``)</em></p>
</div>
<p>The program writes out the following results for the
maximum value of the solution, which should equal 2:</p>
<div class="highlight-text"><div class="highlight"><pre>1.9976  DormandPrince
1.6591  BogackiShampine
1.9812  RKFehlberg
1.0406  Vode
1.9734  RKF45
3.2905  Lsoda
</pre></div>
</div>
<p>The clearly most accurate solver among these is <tt class="docutils literal"><span class="pre">DormandPrince</span></tt>  - the default
method used by MATLAB&#8217;s <tt class="docutils literal"><span class="pre">ode45</span></tt> solver, which is perhaps the world&#8217;s
most popular ODE solver.</p>
<p>The remedy to get all the tested solvers to perform well is to
choose a much stricter tolerance, say <span class="math">\(10^{-10}\)</span>.
Figure <a class="reference internal" href="#gaussian3-fig2-41"><em>Comparison of adaptive methods with error tolerance `E-10`</em></a> shows
coinciding curves. Numerically, we now have</p>
<div class="highlight-text"><div class="highlight"><pre>1.9991  DormandPrince
1.9912  BogackiShampine
1.9942  RKFehlberg
2.0122  Vode
1.9916  RKF45
2.0056  Lsoda
</pre></div>
</div>
<p>For the methods <tt class="docutils literal"><span class="pre">DormandPrince</span></tt>,  <tt class="docutils literal"><span class="pre">RKFehlberg</span></tt>, and
<tt class="docutils literal"><span class="pre">BogackiShampine</span></tt> we have information about the number of adaptive
time points used: 270, 326, and 3307, respectively.</p>
<div class="figure" id="gaussian3-fig2-41">
<a class="reference internal image-reference" href="_images/gaussian3_41_Em10.png"><img alt="_images/gaussian3_41_Em10.png" src="_images/gaussian3_41_Em10.png" style="width: 500px;" /></a>
<p class="caption"><em>Comparison of adaptive methods with error tolerance `E-10`</em></p>
</div>
<p>The lesson learned in this example is two-fold: 1) several methods should
be tested to gain reliability of the results, and Odespy makes such tests
easy to conduct, and 2) strict tolerances, far below
the default values, may be necessary for some methods, here <tt class="docutils literal"><span class="pre">Vode</span></tt> and
<tt class="docutils literal"><span class="pre">Lsoda</span></tt> in particular. We remark that it is the ODE problem that causes
difficulties: changing the problem to <tt class="docutils literal"><span class="pre">odespy.problems.Logistic</span></tt>
(see the file <a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/logistic9.py">logistic9.py</a>)
shows that all the curves coincide and cannot be distinguished visually.</p>
<p>The present test problem with a Gaussian function as solution
can be made increasingly more difficult by
increasing the value of <span class="math">\(c/s\)</span>, i.e., a more peaked function moved to
the right.</p>
</div>
<div class="section" id="extensive-testing">
<h3>Extensive Testing<a class="headerlink" href="#extensive-testing" title="Permalink to this headline">¶</a></h3>
<p>The program <a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/gaussian4.py">gaussian4.py</a>
sets up an extensive experiments involving a lot of solvers, several
<span class="math">\(c/s\)</span> values, and several error tolerances.  The experiments clearly
demonstrate how challenging this ODE problem is for many adaptive
solvers unless <span class="math">\(c/s\)</span> is moderate and a strict tolerance (much lower
than the real accuracy level) is used. One can especially see the
fundamental difficulties that <tt class="docutils literal"><span class="pre">Vode</span></tt>, <tt class="docutils literal"><span class="pre">Lsode</span></tt>, and <tt class="docutils literal"><span class="pre">Lsoda</span></tt> (all stiff
and nonstiff versions) face when <span class="math">\(c/s\geq 8\)</span>: these solvers do not
manage to pick up any variation in <span class="math">\(u\)</span>. Nevertheless, for less
demanding ODEs these solvers may perform very well, but it is highly
recommended to always use the power of the unified Odespy interface
to test several different adaptive methods.</p>
</div>
</div>
<div class="section" id="solving-partial-differential-equations">
<h2>Solving Partial Differential Equations<a class="headerlink" href="#solving-partial-differential-equations" title="Permalink to this headline">¶</a></h2>
<p>Let us now turn the attention to the method of lines for
partial differential equations  (PDEs) where one reduces a PDE to
a system of ODE and then applies standard methods ODEs.</p>
<p>We address a diffusion problem in one dimension:</p>
<div class="math" id="equation-odespy:pde:diffusion">
<span class="eqno">(2)</span>\[     \frac{\partial u(x,t)}{\partial t} = \beta \frac{\partial^{2}u(x,t)}{\partial x^2} + f(x,t), x \in \left(0,L\right),  t \in (0,T],\]</div>
<div class="math" id="equation-odespy:pde:diffusion:x0">
<span class="eqno">(3)</span>\[     u(0,t) = s(t),  t \in (0,T],\]</div>
<div class="math" id="equation-odespy:pde:diffusion:xL">
<span class="eqno">(4)</span>\[     \frac{\partial}{\partial x}u(L,t) = 0, t \in (0,T],\]</div>
<div class="math" id="equation-odespy:pde:diffusion:I">
<span class="eqno">(5)</span>\[     u(x,0) = I(x), x \in \left[0,L\right]\]\[     \thinspace .\]</div>
<div class="section" id="discretization-in-space">
<h3>Discretization in Space<a class="headerlink" href="#discretization-in-space" title="Permalink to this headline">¶</a></h3>
<p>Discretizing the 2nd-order derivative in space with a finite difference,
on a mesh <span class="math">\(x_i=i\Delta x\)</span>, <span class="math">\(i=1,\ldots,N-1\)</span>, gives the ODEs</p>
<div class="math">
\[\frac{\partial u_i(t)}{\partial t} = \beta
\frac{u_{i+1}(t) - 2u_i(t) + u_{i-1}(t)}{\Delta x^2} + f_i(t),\quad i=1,\ldots,N-1
\thinspace .\]</div>
<p>Here we have introduced the notation <span class="math">\(u_i(t)\)</span> as an approximation to the
exact solution at mesh point <span class="math">\(x_i\)</span>.</p>
<p>The boundary condition on <span class="math">\(x=0\)</span>, <span class="math">\(u(0,t)=s(t)\)</span>, gives rise to the ODE</p>
<div class="math">
\[u_0'(t) = s'(t),\quad u_0(0)=s(0)\thinspace .\]</div>
<p>At the other end, <span class="math">\(x=L\)</span>, we use a centered difference approximation
<span class="math">\((u_{N+1}(t)-u_{N-1}(t))/(2\Delta x) =0\)</span> and combine it with the
scheme for <span class="math">\(i=N\)</span> to obtain the modified boundary ODE</p>
<div class="math">
\[\frac{\partial u_N(t)}{\partial t} = \beta
\frac{2u_{N-1}(t) - 2u_N(t)}{\partial x^2} + f_N(t)\thinspace .\]</div>
<p>To summarize, the ODE system reads</p>
<div class="math" id="equation-sec:pde:diff1D:ode1:0">
<span class="eqno">(6)</span>\[     \frac{du_0}{dt} = s'(t),\]</div>
<div class="math" id="equation-sec:pde:diff1D:ode1:i">
<span class="eqno">(7)</span>\[     \frac{du_i}{dt} =  \frac{\beta}{\Delta x^2}
     (u_{i+1}(t) - 2u_i(t) + u_{i-1}(t)) + f_i(t),\quad i=1,\ldots,N-1,\]</div>
<div class="math" id="equation-sec:pde:diff1D:ode1:N">
<span class="eqno">(8)</span>\[     \frac{du_N}{dt} =  \frac{2\beta}{\Delta x^2}
     (u_{N-1}(t) - u_N(t)) + f_i(t)\thinspace .\]</div>
<p>The initial conditions are</p>
<div class="math">
\[u_0(0) = s(0),\]</div>
<div class="math">
\[u_i(0) = I(x_i),\quad i=1,\ldots,N\thinspace .\]</div>
<p>We can apply any method for systems of ODEs to solve
<a href="#equation-sec:pde:diff1D:ode1:0">(6)</a>-<a href="#equation-sec:pde:diff1D:ode1:N">(8)</a>.</p>
</div>
<div class="section" id="implementation">
<h3>Implementation<a class="headerlink" href="#implementation" title="Permalink to this headline">¶</a></h3>
<p>Consider
the evolution of the temperature in a rod modeled by our diffusion problem.
At <span class="math">\(t=0\)</span>, the rod has the temperature 10 C. We then apply a heat source
at <span class="math">\(x=0\)</span> which keepes the temperature there at 50 C. This means that
<span class="math">\(I(x)=283\)</span> K, except at the end point:
<span class="math">\(I(0)=423\)</span> K, <span class="math">\(s(t) = 423\)</span> K, and <span class="math">\(f=0\)</span>.</p>
<p>Odespy solvers need the right-hand side function of
<a href="#equation-sec:pde:diff1D:ode1:0">(6)</a>-<a href="#equation-sec:pde:diff1D:ode1:N">(8)</a>:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">rhs</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">L</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">beta</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="n">N</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">u</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
    <span class="n">dx</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">rhs</span> <span class="o">=</span> <span class="n">zeros</span><span class="p">(</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">rhs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">dsdt</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">N</span><span class="p">):</span>
        <span class="n">rhs</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">u</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="mi">2</span><span class="o">*</span><span class="n">u</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">u</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span> <span class="o">+</span> \
                 <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">t</span><span class="p">)</span>
    <span class="n">rhs</span><span class="p">[</span><span class="n">N</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">u</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="mi">2</span><span class="o">*</span><span class="n">u</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">+</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="n">N</span><span class="p">],</span> <span class="n">t</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">rhs</span>
</pre></div>
</div>
<p>This function requires the variables <tt class="docutils literal"><span class="pre">beta</span></tt>, <tt class="docutils literal"><span class="pre">x</span></tt>, <tt class="docutils literal"><span class="pre">dx</span></tt>, and <tt class="docutils literal"><span class="pre">L</span></tt>, which
we provide as keyword arguments and that can be transferred to <tt class="docutils literal"><span class="pre">rhs</span></tt>
through the <tt class="docutils literal"><span class="pre">f_kwargs</span></tt> argument to the Odespy constructors.</p>
<p>We also need some helper functions</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">s</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
    <span class="k">return</span> <span class="mi">423</span>

<span class="k">def</span> <span class="nf">dsdt</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
    <span class="k">return</span> <span class="mi">0</span>

<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
    <span class="k">return</span> <span class="mi">0</span>
</pre></div>
</div>
<p>Parameters and initial conditions can be set as</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">N</span> <span class="o">=</span> <span class="mi">40</span>
<span class="n">L</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">L</span><span class="p">,</span> <span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
<span class="n">f_kwargs</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">L</span><span class="o">=</span><span class="n">L</span><span class="p">,</span> <span class="n">beta</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="n">x</span><span class="p">)</span>
<span class="n">u</span> <span class="o">=</span> <span class="n">zeros</span><span class="p">(</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>

<span class="n">U_0</span> <span class="o">=</span> <span class="n">zeros</span><span class="p">(</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
<span class="n">U_0</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">s</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">U_0</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span> <span class="o">=</span> <span class="mi">283</span>
</pre></div>
</div>
<p>The construction and execution of a solver is now a matter of</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">odespy</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">odespy</span><span class="o">.</span><span class="n">ForwardEuler</span><span class="p">(</span><span class="n">rhs</span><span class="p">,</span> <span class="n">f_kwargs</span><span class="o">=</span><span class="n">f_kwargs</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">U_0</span><span class="p">)</span>

<span class="n">dx</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">dt</span> <span class="o">=</span> <span class="n">dx</span><span class="o">**</span><span class="mi">2</span><span class="o">/</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">beta</span><span class="p">)</span> <span class="c"># Forward Euler limit</span>
<span class="n">N_t</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">round</span><span class="p">(</span><span class="n">T</span><span class="o">/</span><span class="nb">float</span><span class="p">(</span><span class="n">dt</span><span class="p">)))</span>
<span class="n">time_points</span> <span class="o">=</span> <span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">N_t</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>

<span class="n">u</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>
</pre></div>
</div>
<p>We can add some flexibility and set up several solvers, also
implicit methods:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">odespy</span>
<span class="n">solvers</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">&#39;FE&#39;</span><span class="p">:</span> <span class="n">odespy</span><span class="o">.</span><span class="n">ForwardEuler</span><span class="p">(</span>
        <span class="n">rhs</span><span class="p">,</span> <span class="n">f_kwargs</span><span class="o">=</span><span class="n">f_kwargs</span><span class="p">),</span>
    <span class="s">&#39;BE&#39;</span><span class="p">:</span> <span class="n">odespy</span><span class="o">.</span><span class="n">BackwardEuler</span><span class="p">(</span>
        <span class="n">rhs</span><span class="p">,</span> <span class="n">f_is_linear</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">jac</span><span class="o">=</span><span class="n">K</span><span class="p">,</span>
        <span class="n">f_kwargs</span><span class="o">=</span><span class="n">f_kwargs</span><span class="p">,</span> <span class="n">jac_kwargs</span><span class="o">=</span><span class="n">f_kwargs</span><span class="p">),</span>
    <span class="s">&#39;B2&#39;</span><span class="p">:</span> <span class="n">odespy</span><span class="o">.</span><span class="n">Backward2Step</span><span class="p">(</span>
        <span class="n">rhs</span><span class="p">,</span> <span class="n">f_is_linear</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">jac</span><span class="o">=</span><span class="n">K</span><span class="p">,</span>
        <span class="n">f_kwargs</span><span class="o">=</span><span class="n">f_kwargs</span><span class="p">,</span> <span class="n">jac_kwargs</span><span class="o">=</span><span class="n">f_kwargs</span><span class="p">),</span>
    <span class="s">&#39;theta&#39;</span><span class="p">:</span> <span class="n">odespy</span><span class="o">.</span><span class="n">ThetaRule</span><span class="p">(</span>
        <span class="n">rhs</span><span class="p">,</span> <span class="n">f_is_linear</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">jac</span><span class="o">=</span><span class="n">K</span><span class="p">,</span> <span class="n">theta</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span>
        <span class="n">f_kwargs</span><span class="o">=</span><span class="n">f_kwargs</span><span class="p">,</span> <span class="n">jac_kwargs</span><span class="o">=</span><span class="n">f_kwargs</span><span class="p">),</span>
    <span class="s">&#39;RKF&#39;</span><span class="p">:</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RKFehlberg</span><span class="p">(</span>
        <span class="n">rhs</span><span class="p">,</span> <span class="n">rtol</span><span class="o">=</span><span class="mf">1E-6</span><span class="p">,</span> <span class="n">atol</span><span class="o">=</span><span class="mf">1E-8</span><span class="p">,</span> <span class="n">f_kwargs</span><span class="o">=</span><span class="n">f_kwargs</span><span class="p">),</span>
    <span class="s">&#39;RKC&#39;</span><span class="p">:</span> <span class="n">odespy</span><span class="o">.</span><span class="n">RKC</span><span class="p">(</span>
        <span class="n">rhs</span><span class="p">,</span> <span class="n">rtol</span><span class="o">=</span><span class="mf">1E-6</span><span class="p">,</span> <span class="n">atol</span><span class="o">=</span><span class="mf">1E-8</span><span class="p">,</span> <span class="n">f_kwargs</span><span class="o">=</span><span class="n">f_kwargs</span><span class="p">,</span>
        <span class="n">jac_constant</span><span class="o">=</span><span class="bp">True</span><span class="p">),</span>
    <span class="p">}</span>

<span class="k">try</span><span class="p">:</span>
    <span class="n">method</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
    <span class="n">dt</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
    <span class="n">T</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span>
<span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span>
    <span class="n">method</span> <span class="o">=</span> <span class="s">&#39;FE&#39;</span>
    <span class="n">dx</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">dt</span> <span class="o">=</span> <span class="n">dx</span><span class="o">**</span><span class="mi">2</span><span class="o">/</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">beta</span><span class="p">)</span> <span class="c"># Forward Euler limit</span>
    <span class="k">print</span> <span class="s">&#39;Forward Euler stability limit:&#39;</span><span class="p">,</span> <span class="n">dt</span>
    <span class="n">T</span> <span class="o">=</span> <span class="mf">1.2</span>

<span class="n">solver</span> <span class="o">=</span> <span class="n">solvers</span><span class="p">[</span><span class="n">method</span><span class="p">]</span>
</pre></div>
</div>
<p>The implicit solvers need the Jacobian of the right-hand side function:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">K</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">L</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">beta</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="n">N</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">u</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
    <span class="n">dx</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">K</span> <span class="o">=</span> <span class="n">zeros</span><span class="p">((</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">))</span>
    <span class="n">K</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">N</span><span class="p">):</span>
        <span class="n">K</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span>
        <span class="n">K</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span>
        <span class="n">K</span><span class="p">[</span><span class="n">i</span><span class="p">,</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span>
    <span class="n">K</span><span class="p">[</span><span class="n">N</span><span class="p">,</span><span class="n">N</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="mi">2</span>
    <span class="n">K</span><span class="p">[</span><span class="n">N</span><span class="p">,</span><span class="n">N</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">K</span>
</pre></div>
</div>
<p>Note that we work with <em>dense square matrices</em> while the mathematics
allows a tridiagonal matrix and corresponding solver.
However, in 1D problems, the computations
are so fast anyway so we can live with dense matrices.</p>
<p>Finally, some animation of the solution is desirable:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="c"># Make movie</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s">&#39;rm tmp_*.png&#39;</span><span class="p">)</span>  <span class="c"># remove old plot files</span>
<span class="n">plt</span><span class="o">.</span><span class="n">figure</span><span class="p">()</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ion</span><span class="p">()</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">u</span><span class="p">[</span><span class="mi">0</span><span class="p">,:]</span>
<span class="n">lines</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">axis</span><span class="p">([</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">x</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="mi">273</span><span class="p">,</span> <span class="mf">1.2</span><span class="o">*</span><span class="n">s</span><span class="p">(</span><span class="mi">0</span><span class="p">)])</span>
<span class="n">plt</span><span class="o">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s">&#39;x&#39;</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s">&#39;u(x,t)&#39;</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">u</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]):</span>
    <span class="k">print</span> <span class="n">t</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
    <span class="n">lines</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_ydata</span><span class="p">(</span><span class="n">u</span><span class="p">[</span><span class="n">i</span><span class="p">,:])</span>
    <span class="n">plt</span><span class="o">.</span><span class="n">legend</span><span class="p">([</span><span class="s">&#39;t=</span><span class="si">%.3f</span><span class="s">&#39;</span> <span class="o">%</span> <span class="n">t</span><span class="p">[</span><span class="n">i</span><span class="p">]])</span>
    <span class="n">plt</span><span class="o">.</span><span class="n">draw</span><span class="p">()</span>
    <span class="k">if</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="c"># plot every 5 steps</span>
        <span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s">&#39;tmp_</span><span class="si">%04d</span><span class="s">.png&#39;</span> <span class="o">%</span> <span class="n">counter</span><span class="p">)</span>
        <span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="c">#time.sleep(0.2)</span>
</pre></div>
</div>
<div class="section" id="vectorized-code">
<h4>Vectorized Code<a class="headerlink" href="#vectorized-code" title="Permalink to this headline">¶</a></h4>
<p>It is easy to get rid of the loops in the <tt class="docutils literal"><span class="pre">rhs</span></tt> and <tt class="docutils literal"><span class="pre">K</span></tt> functions:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">rhs_vec</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">L</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">beta</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="n">N</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">u</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
    <span class="n">dx</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">rhs</span> <span class="o">=</span> <span class="n">zeros</span><span class="p">(</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">rhs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">dsdt</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
    <span class="n">rhs</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="n">N</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">u</span><span class="p">[</span><span class="mi">2</span><span class="p">:</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="mi">2</span><span class="o">*</span><span class="n">u</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="n">N</span><span class="p">]</span> <span class="o">+</span> <span class="n">u</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="n">N</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span> <span class="o">+</span> \
               <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="n">N</span><span class="p">],</span> <span class="n">t</span><span class="p">)</span>
    <span class="n">i</span> <span class="o">=</span> <span class="n">N</span>
    <span class="n">rhs</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">u</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="mi">2</span><span class="o">*</span><span class="n">u</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">+</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="n">N</span><span class="p">],</span> <span class="n">t</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">rhs</span>

<span class="k">def</span> <span class="nf">K_vec</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">L</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">beta</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Vectorized computation of K.&quot;&quot;&quot;</span>
    <span class="n">N</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">u</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
    <span class="n">dx</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">K</span> <span class="o">=</span> <span class="n">zeros</span><span class="p">((</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">))</span>
    <span class="n">K</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">K</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="n">N</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span>
    <span class="n">K</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="n">N</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span>
    <span class="n">K</span><span class="p">[</span><span class="mi">2</span><span class="p">:</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span>
    <span class="n">K</span><span class="p">[</span><span class="n">N</span><span class="p">,</span><span class="n">N</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="mi">2</span>
    <span class="n">K</span><span class="p">[</span><span class="n">N</span><span class="p">,</span><span class="n">N</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">beta</span><span class="o">/</span><span class="n">dx</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">K</span>
</pre></div>
</div>
</div>
</div>
<div class="section" id="experiments">
<h3>Experiments<a class="headerlink" href="#experiments" title="Permalink to this headline">¶</a></h3>
<p>The program, <a class="reference external" href="https://github.com/hplgit/odespy/blob/master/doc/src/odespy/src-odespy/pde_diffusion.py">pde_diffusion.py</a>, can be run to test different solvers and illustrate numerical
methods:</p>
<p><strong>Forward Euler Method.</strong>
Run</p>
<div class="highlight-text"><div class="highlight"><pre>python pde_diffusion.py
</pre></div>
</div>
<p>The graphics takes very long time in this simulation, because of the
small time step.</p>
<p><strong>Backward Euler.</strong>
Run</p>
<div class="highlight-text"><div class="highlight"><pre>python ode_diffusion.py BE 0.05 1.2
</pre></div>
</div>
<p><strong>Backward 2-step Method.</strong>
Run</p>
<div class="highlight-text"><div class="highlight"><pre>python ode_diffusion.py B2 0.05 1.2
</pre></div>
</div>
<p><strong>Crank-Nicolson Method.</strong>
Run</p>
<div class="highlight-text"><div class="highlight"><pre>python ode_diffusion.py theta 0.01 1.2
</pre></div>
</div>
<p>Observe the non-physical oscillations because of the steep initial
condition (and the lack of damping in the Crank-Nicolson scheme).</p>
<p><strong>Runge-Kutta-Fehlberg Method.</strong>
Run</p>
<div class="highlight-text"><div class="highlight"><pre>python ode_diffusion.py RKF 0.01 1.2
</pre></div>
</div>
<p>Note here that we specify a quite large <span class="math">\(\Delta t\)</span>, much larger than
what a Runge-Kutta method can work with (typically, an RK4 method
needs a <span class="math">\(\Delta t\)</span> as small as the critical step for the Forward Euler
method). However, the adaptive method figures out what it needs of
steps and produces a nice solution.</p>
<p><strong>Runge-Kutta-Chebyshev Method.</strong>
Run</p>
<div class="highlight-text"><div class="highlight"><pre>python ode_diffusion.py RKC 0.05 1.2
</pre></div>
</div>
<p>This is a promising method for the diffusion equation. It works like
an explicit method and can tolerate large time steps. This method calls
up the FORTRAN code <tt class="docutils literal"><span class="pre">rkc.f</span></tt>.</p>
</div>
</div>
<div class="section" id="inner-workings-of-the-package">
<h2>Inner Workings of the Package<a class="headerlink" href="#inner-workings-of-the-package" title="Permalink to this headline">¶</a></h2>
<p>There are three basic entities when solving ODEs numerically: the
definition of the <em>ODE system</em>, the <em>time-stepping method</em>, and
the <em>solver</em> that runs the &#8220;time loop&#8221; and stores results.</p>
<p>The information about the ODE system is made very simple: the user
provides 1) an object that can be called as a Python function <tt class="docutils literal"><span class="pre">f(u,</span>
<span class="pre">t)</span></tt>, and 2) an array or list with the initial values.  Some users will
store this information in their own data structures, e.g., a class.</p>
<p>The time-stepping method and the algorithm for calling the time-stepping
are collected in a solver class. All the solver classes are
related in a class hierarchy. Each solver class initialized by the right-hand
side function (<tt class="docutils literal"><span class="pre">f</span></tt>) and an optional set of parameters for controlling
various parts of the solution process. The solver object is also
used to set the initial condition (<tt class="docutils literal"><span class="pre">set_initial_condition</span></tt>) and
to run the solution process (<tt class="docutils literal"><span class="pre">solve</span></tt>). The time-stepping scheme
is normally isolated in a method <tt class="docutils literal"><span class="pre">advance</span></tt> in the solver classes, but
for some schemes or external software packages the separation of
doing one step and doing the whole time integration is less feasible.
In those cases, <tt class="docutils literal"><span class="pre">solve</span></tt> will mix the time-stepping loop and the
numerical scheme.</p>
<p>The package does not interact with visualization tools - the array
containing the solution is returned to the user and must be further
processed and visualized in the user&#8217;s code.</p>
<p>Below we describe how the classes in the solver hierarchy work and how
parameters are registered and initialized.</p>
<div class="section" id="solver-parameters">
<span id="odes-parameters"></span><h3>Solver Parameters<a class="headerlink" href="#solver-parameters" title="Permalink to this headline">¶</a></h3>
<p>The <tt class="docutils literal"><span class="pre">solver</span></tt> module defines a global dictionary <tt class="docutils literal"><span class="pre">_parameters</span></tt> holding
all legal parameters in Odespy classes. These are parameters that
the user can adjust. Other modules imports this <tt class="docutils literal"><span class="pre">_parameters</span></tt> dict
and updates it with their own additional parameters.</p>
<p>For each parameter the <tt class="docutils literal"><span class="pre">_parameters</span></tt> dict stores the parameter name, a
default value, a description, the legal object type for the value of
the parameter, and other quantities if needed. A typical example
is</p>
<div class="highlight-text"><div class="highlight"><pre>_parameters = dict(
...

f = dict(
    help=&#39;Right-hand side f(u,t) defining the ODE&#39;,
    type=callable),

f_kwargs = dict(
    help=&#39;Extra keyword arguments to f: f(u, t, *f_args, **f_kwargs)&#39;,
    type=dict,
    default={}),

theta = dict(
    help=&#39;eight in [0,1] used for&#39;\
         &#39;&quot;theta-rule&quot; finite difference approx.&#39;,
    default=0.5,
    type=(int,float),
    range=[0, 1])

...
}
</pre></div>
</div>
<p>Each solver class defines a (static) class variable
<tt class="docutils literal"><span class="pre">_required_parameters</span></tt> for holding the names of all required
parameters (in a list). In addition, each solver class defines another
class variable <tt class="docutils literal"><span class="pre">_optional_parameters</span></tt> holding the names of all the
optional parameters. The doc strings of the solver classes are
automatically equipped with tables of required and optional
parameters.</p>
<p>The optional parameters of a class consist of the optional parameters
of the superclass and those specific to the class. The typical
initialization of <tt class="docutils literal"><span class="pre">_optional_parameters</span></tt> goes like this:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">SomeMethod</span><span class="p">(</span><span class="n">ParentMethod</span><span class="p">):</span>
    <span class="n">_optional_parameters</span> <span class="o">=</span> <span class="n">ParentMethod</span><span class="o">.</span><span class="n">_optional_parameters</span> <span class="o">+</span> \
                           <span class="p">[</span><span class="s">&#39;prm1&#39;</span><span class="p">,</span> <span class="s">&#39;prm2&#39;</span><span class="p">,</span> <span class="o">...</span><span class="p">]</span>
</pre></div>
</div>
<p>where <tt class="docutils literal"><span class="pre">prm1</span></tt>, <tt class="docutils literal"><span class="pre">prm2</span></tt>, etc. are names registered in the global
<tt class="docutils literal"><span class="pre">_parameters</span></tt> dictionary.</p>
<p>From a user&#8217;s point of view, the parameters are set either at
construction time or through the <tt class="docutils literal"><span class="pre">set</span></tt> function:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">odespy</span> <span class="kn">import</span> <span class="n">RK2</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
<span class="gp">... </span>  <span class="k">return</span> <span class="n">a</span><span class="o">*</span><span class="n">u</span> <span class="o">+</span> <span class="n">b</span>
<span class="gp">...</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">solver</span> <span class="o">=</span> <span class="n">RK2</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">f_kwargs</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span><span class="n">b</span><span class="o">=</span><span class="mi">1</span><span class="p">))</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">solver</span><span class="o">.</span><span class="n">f_kwargs</span>
<span class="go">{&#39;b&#39;: 1}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">solver</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">f_args</span><span class="o">=</span><span class="p">(</span><span class="mi">3</span><span class="p">,))</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">solver</span><span class="o">.</span><span class="n">f_args</span>
<span class="go">(3,)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="c"># Get all registered parameters in the method instance</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">solver</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
<span class="go">{&#39;f_kwargs&#39;: {&#39;b&#39;: 1}, &#39;f_args&#39;: (3,), &#39;complex_valued&#39;: False,</span>
<span class="go">&#39;name of f&#39;: &#39;f&#39;}</span>
</pre></div>
</div>
<p>The <tt class="docutils literal"><span class="pre">set</span></tt> method sets parameters through keyword arguments and can
take an arbitrary collection of such arguments:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">solver</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">name1</span><span class="o">=</span><span class="n">value1</span><span class="p">,</span> <span class="n">name2</span><span class="o">=</span><span class="n">value2</span><span class="p">,</span> <span class="n">name3</span><span class="o">=</span><span class="n">value3</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>
</pre></div>
</div>
<p>The <tt class="docutils literal"><span class="pre">get</span></tt> method returns the parameters and their values as a dictionary.
We remark that the <tt class="docutils literal"><span class="pre">'f'</span></tt> key, which one might expect to appear in the
returned dictionary of parameters, are omitted because it is always
a lambda function wrapping the user&#8217;s <tt class="docutils literal"><span class="pre">f</span></tt> function such that the
returned value is guaranteed to be a <tt class="docutils literal"><span class="pre">numpy</span></tt> array. Instead,
there is an entry <tt class="docutils literal"><span class="pre">'name</span> <span class="pre">of</span> <span class="pre">f'</span></tt> which reflects the name of the
user-supplied function. The same comment applies to the <tt class="docutils literal"><span class="pre">jac</span></tt> parameter
for specifying the Jacobian used in implicit methods.</p>
</div>
<div class="section" id="solver-classes">
<h3>Solver Classes<a class="headerlink" href="#solver-classes" title="Permalink to this headline">¶</a></h3>
<p>Each solver in this package is implemented as a class in a class hierarchy.
Basic, common functionality is inherited from super classes, and the
actual solver class implements what is specific for the method in question.</p>
<div class="section" id="the-inherited-superclass-constructor">
<h4>The Inherited Superclass Constructor<a class="headerlink" href="#the-inherited-superclass-constructor" title="Permalink to this headline">¶</a></h4>
<p>Class <tt class="docutils literal"><span class="pre">Solver</span></tt> is the super class of the hierarchy. Subclasses
normally just inherit their constructor from class <tt class="docutils literal"><span class="pre">Solver</span></tt>. This
constructor requires one mandatory argument: the right-hand side of
the ODE, <span class="math">\(f(u,t)\)</span>, coded as a Python function <tt class="docutils literal"><span class="pre">f(u,</span> <span class="pre">t)</span></tt>.
Some solvers implemented in FORTRAN allows <tt class="docutils literal"><span class="pre">f</span></tt> to be a wrapper of
a FORTRAN function defining the right-hand side, but such a wrapper
is specified through the <tt class="docutils literal"><span class="pre">f_f77</span></tt> argument and using <tt class="docutils literal"><span class="pre">None</span></tt> for <tt class="docutils literal"><span class="pre">f</span></tt>.
The constructor accepts a range of
additional keyword arguments for setting
parameters of the solver. Which keyword arguments that are
available depends on what the subclass has registered as legal
parameters in <tt class="docutils literal"><span class="pre">_optional_parameters</span></tt> and <tt class="docutils literal"><span class="pre">_required_parameters</span></tt>.</p>
<p>The constructor performs a set of tasks that are common to all
the subclass solvers:</p>
<ol class="arabic simple">
<li>The set of optional and required parameters of a particular solver
is loaded into <tt class="docutils literal"><span class="pre">self._parameters</span></tt> such that this dictionary
can be used to look up all parameters of the solver.</li>
<li>The solver-specific method <tt class="docutils literal"><span class="pre">adjust_parameters</span></tt> is called to allow
the programmer of a solver to manipulate <tt class="docutils literal"><span class="pre">self._parameters</span></tt>.
For example, some parameters may be modified or set
according to the value of others.</li>
<li>Entries in <tt class="docutils literal"><span class="pre">self._parameters</span></tt> are mirrored by class
attributes. The computations and the <tt class="docutils literal"><span class="pre">set</span></tt> and <tt class="docutils literal"><span class="pre">get</span></tt> methods will
make use of the attributes rather than the <tt class="docutils literal"><span class="pre">self._parameters</span></tt> dict
to extract data.  For example, the value of
<tt class="docutils literal"><span class="pre">self._parameters['myvar']</span></tt> becomes available as <tt class="docutils literal"><span class="pre">self.myvar</span></tt> and
in the algorithms we use <tt class="docutils literal"><span class="pre">self.myvar</span></tt>, perhaps with a test
<tt class="docutils literal"><span class="pre">hasattr(self,</span> <span class="pre">'myvar')</span></tt> or a <tt class="docutils literal"><span class="pre">try-except</span></tt> clause (catching
an <tt class="docutils literal"><span class="pre">AttributeError</span></tt>).</li>
<li>The <tt class="docutils literal"><span class="pre">set</span></tt> method is called with all keyword arguments given to the
constructor, which then modifies the default values of the
parameters and sets the corresponding attributes.</li>
<li>The <tt class="docutils literal"><span class="pre">f</span></tt> function is wrapped in a lambda function such that
<tt class="docutils literal"><span class="pre">f(u,</span> <span class="pre">t)</span></tt> is guaranteed to return an array (in case the user
returns a list or scalar for convenience). The same is done with
the Jacobian (<tt class="docutils literal"><span class="pre">jac</span></tt>) and other user-defined callback functions.</li>
<li>The <tt class="docutils literal"><span class="pre">initialize</span></tt> method is called to finalize the tasks in
the constructor. The most common use of this method in subclasses
is to import extension modules that the solver depends on and
provide an error message if the extension modules are not available.
If they are, the modules are normally stored through an attribute
of the subclass.</li>
</ol>
</div>
<div class="section" id="useful-methods">
<h4>Useful Methods<a class="headerlink" href="#useful-methods" title="Permalink to this headline">¶</a></h4>
<p>Let <tt class="docutils literal"><span class="pre">solver</span></tt> be some instance of a subclass in the hierarchy. The
following methods are sometimes useful:</p>
<blockquote>
<div><ul class="simple">
<li><tt class="docutils literal"><span class="pre">repr(solver)</span></tt>: return the subclass name along with all
registered parameters and their values. This string provides
complete information on the initialization of a solver.</li>
<li><tt class="docutils literal"><span class="pre">str(solver)</span></tt>: return a short pretty print string reflecting
the name of the method and the value of parameters that
must be known to uniquely define the numerical method.
This string, or the class name as given by <tt class="docutils literal"><span class="pre">solver.name()</span></tt>,
is useful for the legend in a plot or as method identifier in a table.</li>
<li><tt class="docutils literal"><span class="pre">solver.get_parameter_info()</span></tt>: return or print all registered
parameters for the current solver and all properties for
each parameter.</li>
</ul>
</div></blockquote>
<ol class="arabic simple" start="7">
<li><tt class="docutils literal"><span class="pre">solver.switch_to(name)</span></tt>: return a new solver of type <tt class="docutils literal"><span class="pre">name</span></tt>,
initialized with all
parameters of the current solver that are legal in the new solver.
The method is useful when trying out a range of solvers for a
problem.</li>
</ol>
</div>
<div class="section" id="the-solve-method">
<h4>The Solve Method<a class="headerlink" href="#the-solve-method" title="Permalink to this headline">¶</a></h4>
<p>After the constructor is called, <tt class="docutils literal"><span class="pre">solver.set_initial_condition</span></tt> is
called to set the initial condition, and then <tt class="docutils literal"><span class="pre">solve</span></tt> is called.
The <tt class="docutils literal"><span class="pre">solve</span></tt> method features the following steps:</p>
<ol class="arabic simple">
<li>Call <tt class="docutils literal"><span class="pre">initialize_for_solve</span></tt> (implemented in subclasses) to
precompute whatever is needed before the time loop.
The super class allocates storage for the solution and
loads the initial condition into that data structure.
Any subclass implementation of <tt class="docutils literal"><span class="pre">initialize_for_solve</span></tt> must therefore
also call this method in its super class.</li>
<li>Call <tt class="docutils literal"><span class="pre">validate_data</span></tt> to check if the data structures are consistent
before starting the computations. Subclass implementations of
this method must call the super class&#8217; version of the method.</li>
<li>Run a loop over all time levels <tt class="docutils literal"><span class="pre">n</span></tt> and call <tt class="docutils literal"><span class="pre">advance</span></tt> (implemented
in subclasses) at each level to advance the solution from
time <tt class="docutils literal"><span class="pre">t[n]</span></tt> to <tt class="docutils literal"><span class="pre">t[n+1]</span></tt>. Also call <tt class="docutils literal"><span class="pre">terminate</span></tt> so that the
user code can analyze, work with the solution, and terminate the
solution process.</li>
</ol>
<p>Some subclasses will override the <tt class="docutils literal"><span class="pre">solve</span></tt> method and provide their own,
but most subclasses just inherits the general one and implements
the <tt class="docutils literal"><span class="pre">advance</span></tt> method.</p>
</div>
<div class="section" id="solver-attributes">
<h4>Solver Attributes<a class="headerlink" href="#solver-attributes" title="Permalink to this headline">¶</a></h4>
<p>All classes have a set of attributes:</p>
<ol class="arabic simple">
<li><tt class="docutils literal"><span class="pre">users_f</span></tt>: holds the user&#8217;s function for <span class="math">\(f(u, t)\)</span>.
Implicit solvers may have a corresponding <tt class="docutils literal"><span class="pre">users_jac</span></tt> for
the user&#8217;s Jacobian.</li>
<li>One attribute for each parameter in the class.</li>
<li><tt class="docutils literal"><span class="pre">u</span></tt>: 1D <tt class="docutils literal"><span class="pre">numpy</span></tt> array holding the solution for a scalar ODE and
a 2D array in case of a system of ODEs. The first index
denotes the time level.</li>
<li><tt class="docutils literal"><span class="pre">t</span></tt>: the time levels corresponding to the first index in the <tt class="docutils literal"><span class="pre">u</span></tt> array.</li>
<li><tt class="docutils literal"><span class="pre">quick_description</span></tt>: a short one-line description of the method (this
variable is static in the class, i.e., declared outside any method).</li>
</ol>
<p>Most classes will also define two additional static variables,
<tt class="docutils literal"><span class="pre">_required_parameters</span></tt> and <tt class="docutils literal"><span class="pre">_optional_parameters</span></tt> as explained
in the section <a class="reference internal" href="#odes-parameters"><em>Solver Parameters</em></a>.</p>
</div>
<div class="section" id="other-superclasses">
<h4>Other Superclasses<a class="headerlink" href="#other-superclasses" title="Permalink to this headline">¶</a></h4>
<p>There are superclasses <tt class="docutils literal"><span class="pre">SolverImplicit</span></tt> for implicit methods,
<tt class="docutils literal"><span class="pre">Adaptive</span></tt> for adaptive methods, <tt class="docutils literal"><span class="pre">RungeKutta1level</span></tt> for general,
explicit 1-level Runge-Kutta methods, <tt class="docutils literal"><span class="pre">RungeKutta2level</span></tt> for
general, explicit, adaptive 2-level Runge-Kutta methods,
<tt class="docutils literal"><span class="pre">ode_scipy</span></tt> for interfaces to ODE solvers
in <tt class="docutils literal"><span class="pre">scipy</span></tt>, and <tt class="docutils literal"><span class="pre">Odepack</span></tt> for interfaces to the ODEPACK family
of solvers.</p>
</div>
</div>
<div class="section" id="a-very-simple-subclass">
<h3>A Very Simple Subclass<a class="headerlink" href="#a-very-simple-subclass" title="Permalink to this headline">¶</a></h3>
<p>To implement a simple explicit scheme for solving a scalar ODE or a system
of ODEs, you only need to write a subclass of <tt class="docutils literal"><span class="pre">Solver</span></tt> with an
<tt class="docutils literal"><span class="pre">advance</span></tt> method containing the formula that updates the solution from
one time level to the next. For example, the Forward Euler scheme
reads</p>
<div class="math">
\[u_{n+1} = u_n + \Delta t f(u_n, t_n),\]</div>
<p>where subscript <span class="math">\(n\)</span> denotes the time level, and <span class="math">\(\Delta t = t_{n+1}-t_n\)</span> is
the current time step.
The implementation goes like</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">ForwardEuler</span><span class="p">(</span><span class="n">Solver</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">    Forward Euler scheme::</span>

<span class="sd">        u[n+1] = u[n] + dt*f(u[n], t[n])</span>
<span class="sd">    &quot;&quot;&quot;</span>
    <span class="n">quick_description</span> <span class="o">=</span> <span class="s">&#39;The simple explicit (forward) Euler scheme&#39;</span>

    <span class="k">def</span> <span class="nf">advance</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">u</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">u</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">f</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">n</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">t</span>
        <span class="n">dt</span> <span class="o">=</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="p">]</span>
        <span class="n">unew</span> <span class="o">=</span> <span class="n">u</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">+</span> <span class="n">dt</span><span class="o">*</span><span class="n">f</span><span class="p">(</span><span class="n">u</span><span class="p">[</span><span class="n">n</span><span class="p">],</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="p">])</span>
        <span class="k">return</span> <span class="n">unew</span>
</pre></div>
</div>
<p>Remarks:</p>
<ol class="arabic simple">
<li>The <tt class="docutils literal"><span class="pre">quick_description</span></tt> string is necessary for the class to appear in the
automatically generated overview of implemented methods
(run <tt class="docutils literal"><span class="pre">pydoc</span> <span class="pre">odespy</span></tt> to see this table).</li>
<li>Extracting class attributes in local variables (here <tt class="docutils literal"><span class="pre">u</span></tt>, <tt class="docutils literal"><span class="pre">f</span></tt>, etc.)
avoids the need for the <tt class="docutils literal"><span class="pre">self</span></tt> prefix so that the implemented formulas
are as close to the mathematical formulas as possible.</li>
</ol>
<p>Almost equally simple schemes, like explicit Runge-Kutta methods and Heun&#8217;s
method are implemented in the same way (see <a class="reference external" href="https://github.com/hplgit/odespy/tree/master/odespy/odespy/solvers.py">solvers.py</a>).</p>
</div>
<div class="section" id="a-subclass-with-more-code">
<h3>A Subclass with More Code<a class="headerlink" href="#a-subclass-with-more-code" title="Permalink to this headline">¶</a></h3>
<p>A 2nd-order Adams-Bashforth scheme is a bit more complicated since it
involves three time levels and therefore needs a separate method for
the first step. We should also avoid unnecessary evaluations of <span class="math">\(f(u,t)\)</span>.
The user can specify a parameter <tt class="docutils literal"><span class="pre">start_method</span></tt> for the name of the
solver to be used for the first step. This solver is initialized
by the <tt class="docutils literal"><span class="pre">switch_to</span></tt> method in class <tt class="docutils literal"><span class="pre">Solver</span></tt>. Basically,</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">new_solver</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">switch_to</span><span class="p">(</span><span class="n">solver_name</span><span class="p">)</span>
</pre></div>
</div>
<p>creates a new solver instance <tt class="docutils literal"><span class="pre">new_solver</span></tt>, of the class implied by
<tt class="docutils literal"><span class="pre">solver_name</span></tt>, where all relevant parameters from <tt class="docutils literal"><span class="pre">solver</span></tt> are copied
to <tt class="docutils literal"><span class="pre">new_solver</span></tt>.</p>
<p>An implementation of a subclass for the
2nd-order Adams-Bashforth scheme can then look as follows.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">AdamsBashforth2</span><span class="p">(</span><span class="n">Solver</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">    Second-order Adams-Bashforth method::</span>

<span class="sd">        u[n+1] = u[n] + dt/2.*(3*f(u[n], t[n]) - f(u[n-1], t[n-1]))</span>

<span class="sd">    for constant time step dt.</span>

<span class="sd">    RK2 is used as default solver in first step.</span>
<span class="sd">    &quot;&quot;&quot;</span>
    <span class="n">quick_description</span> <span class="o">=</span> <span class="s">&quot;Explicit 2nd-order Adams-Bashforth method&quot;</span>

    <span class="n">_optional_parameters</span> <span class="o">=</span> <span class="n">Solver</span><span class="o">.</span><span class="n">_optional_parameters</span> <span class="o">+</span> \
                          <span class="p">[</span><span class="s">&#39;start_method&#39;</span><span class="p">,]</span>

    <span class="k">def</span> <span class="nf">initialize_for_solve</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="c"># New solver instance for first step</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">starter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">switch_to</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">start_method</span><span class="p">)</span>
        <span class="n">Solver</span><span class="o">.</span><span class="n">initialize_for_solve</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">validate_data</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">constant_time_step</span><span class="p">():</span>
            <span class="k">print</span> <span class="s">&#39;</span><span class="si">%s</span><span class="s"> must have constant time step&#39;</span> <span class="o">%</span> <span class="bp">self</span><span class="o">.</span><span class="n">__name__</span>
            <span class="k">return</span> <span class="bp">False</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">True</span>

    <span class="k">def</span> <span class="nf">advance</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">u</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">u</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">f</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">n</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">t</span>

        <span class="k">if</span> <span class="n">n</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">:</span>
            <span class="n">dt</span> <span class="o">=</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="p">]</span>  <span class="c"># must be constant</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">f_n</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">u</span><span class="p">[</span><span class="n">n</span><span class="p">],</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="p">])</span>
            <span class="n">unew</span> <span class="o">=</span> <span class="n">u</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">+</span> <span class="n">dt</span><span class="o">/</span><span class="mf">2.</span><span class="o">*</span><span class="p">(</span><span class="mi">3</span><span class="o">*</span><span class="bp">self</span><span class="o">.</span><span class="n">f_n</span> <span class="o">-</span> <span class="bp">self</span><span class="o">.</span><span class="n">f_n_1</span><span class="p">)</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">f_n_1</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">f_n</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="c"># User-specified method for the first step</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">starter</span><span class="o">.</span><span class="n">set_initial_condition</span><span class="p">(</span><span class="n">u</span><span class="p">[</span><span class="n">n</span><span class="p">])</span>
            <span class="n">time_points</span> <span class="o">=</span> <span class="p">[</span><span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="p">],</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">]]</span>
            <span class="n">u_starter</span><span class="p">,</span> <span class="n">t_starter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">starter</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">time_points</span><span class="p">)</span>
            <span class="n">unew</span> <span class="o">=</span> <span class="n">u_starter</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">f_n_1</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">u</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">t</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>

        <span class="k">return</span> <span class="n">unew</span>
</pre></div>
</div>
<p>Three features are worth comments: 1) we extend the set of optional
parameters; 2) we must initialize a separate solver for the first
step, and this is done in the <tt class="docutils literal"><span class="pre">initialize_for_solve</span></tt> method that will
be called as part of <tt class="docutils literal"><span class="pre">solve</span></tt> before the time stepping; and 3) we
extend <tt class="docutils literal"><span class="pre">validate_data</span></tt> to check that the time spacing given by the
<tt class="docutils literal"><span class="pre">time_points</span></tt> argument to <tt class="docutils literal"><span class="pre">solve</span></tt> is constant. The utility method
<tt class="docutils literal"><span class="pre">constant_time_step</span></tt> provided in super class <tt class="docutils literal"><span class="pre">Solver</span></tt> carries out the
details of the check.</p>
<p>More advanced implementations of subclasses can be studied
in the <a class="reference external" href="https://github.com/hplgit/odespy/tree/master/odespy/odespy/solvers.py">solvers.py</a> and <a class="reference external" href="https://github.com/hplgit/odespy/tree/master/odespy/odespy/RungeKutta.py">RungeKutta.py</a> files.</p>
</div>
<div class="section" id="a-simple-example-of-an-implicit-method">
<h3>A Simple Example of an Implicit Method<a class="headerlink" href="#a-simple-example-of-an-implicit-method" title="Permalink to this headline">¶</a></h3>
<p>Class <tt class="docutils literal"><span class="pre">SolverImplicit</span></tt> acts as superclass for the implementation of
implicit methods. This class provides some basic functionality for
solving the system of nonlinear equations that normally arises in
implicit methods by Picard or Newton iteration.
The parameter <tt class="docutils literal"><span class="pre">nonlinear_solver</span></tt> can take the values <tt class="docutils literal"><span class="pre">Picard</span></tt> or
<tt class="docutils literal"><span class="pre">Newton</span></tt>. The user must in case of Newton&#8217;s method provide
a <tt class="docutils literal"><span class="pre">jac</span></tt> parameter for a function evaluating the Jacobian of <span class="math">\(f(u,t)\)</span>
with respect to <span class="math">\(u\)</span>: <span class="math">\(J_{i,j} = \partial f_i/\partial u_j\)</span>.</p>
<p>Instead of implementing an <tt class="docutils literal"><span class="pre">advance</span></tt> method in subclasses, one provides
a method <tt class="docutils literal"><span class="pre">Picard</span></tt> and/or <tt class="docutils literal"><span class="pre">Newton</span></tt> to define key quantities in these
methods. The superclass implements <tt class="docutils literal"><span class="pre">advance</span></tt>, which will run a Picard
or Newton iteration. The <tt class="docutils literal"><span class="pre">Picard</span></tt> method returns all the terms
on the right-hand side of the discrete equation when only <tt class="docutils literal"><span class="pre">u[n+1]</span></tt> is
on the left-hand side. <tt class="docutils literal"><span class="pre">Newton</span></tt> returns the right-hand side and the
Jacobian of the system to be solved in each Newton iteration.</p>
<p>Here is an example showing the complete code of the Backward Euler method.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">BackwardEuler</span><span class="p">(</span><span class="n">SolverImplicit</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">    Implicit Backward Euler method::</span>

<span class="sd">       u[n+1] = u[n] + dt*f(t[n+1], u[n+1])</span>

<span class="sd">    The nonlinear system is solved by Newton or Picard iteration.</span>
<span class="sd">    &quot;&quot;&quot;</span>
    <span class="n">quick_description</span> <span class="o">=</span> <span class="s">&quot;Implicit 1st-order Backward Euler method&quot;</span>

    <span class="k">def</span> <span class="nf">Picard_update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">ukp1</span><span class="p">):</span>
        <span class="n">u</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">u</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">f</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">n</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">t</span>
        <span class="n">dt</span> <span class="o">=</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="p">]</span>
        <span class="k">return</span> <span class="n">u</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">+</span> <span class="n">dt</span><span class="o">*</span><span class="n">f</span><span class="p">(</span><span class="n">ukp1</span><span class="p">,</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">])</span>

    <span class="k">def</span> <span class="nf">Newton_system</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">ukp1</span><span class="p">):</span>
        <span class="n">u</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">t</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">u</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">f</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">n</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">t</span>
        <span class="n">dt</span> <span class="o">=</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="p">]</span>
        <span class="n">F</span> <span class="o">=</span> <span class="n">ukp1</span> <span class="o">-</span> <span class="p">(</span><span class="n">u</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">+</span> <span class="n">dt</span><span class="o">*</span><span class="n">f</span><span class="p">(</span><span class="n">ukp1</span><span class="p">,</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">]))</span>
        <span class="n">J</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">eye</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">neq</span><span class="p">)</span> <span class="o">-</span> <span class="n">dt</span><span class="o">*</span><span class="bp">self</span><span class="o">.</span><span class="n">jac</span><span class="p">(</span><span class="n">ukp1</span><span class="p">,</span> <span class="n">t</span><span class="p">[</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">])</span>
        <span class="k">return</span> <span class="n">F</span><span class="p">,</span> <span class="n">J</span>
</pre></div>
</div>
</div>
</div>
<div class="section" id="troubleshooting">
<h2>Troubleshooting<a class="headerlink" href="#troubleshooting" title="Permalink to this headline">¶</a></h2>
<div class="section" id="constructor-takes-exactly-two-arguments-5-given">
<h3>Constructor takes exactly two arguments, 5 given<a class="headerlink" href="#constructor-takes-exactly-two-arguments-5-given" title="Permalink to this headline">¶</a></h3>
<p>Constructors in the <tt class="docutils literal"><span class="pre">Solver</span></tt> hierarchy take only the <tt class="docutils literal"><span class="pre">f</span></tt> function
as positional argument. All other parameters to the constructor
must be keyword arguments.</p>
<p><strong>Remark.</strong>
This document was written with aid of the
<a class="reference external" href="https://github.com/hplgit/doconce">DocOnce</a> tool, which allows
output in many different formats.</p>
</div>
</div>
</div>


          </div>
        </div>
      </div>
      <div class="sphinxsidebar">
        <div class="sphinxsidebarwrapper">
            <center>
            <p class="logo"><a href="http://cbc.simula.no/" title="Go to Center for Biomedical Computing">
              <img class="logo" src="_static/cbc_logo.png" alt="Logo"/>
            </a></p>
            </center>
  <h3><a href="index.html">Table Of Contents</a></h3>
  <ul>
<li><a class="reference internal" href="#">A Tutorial for the Odespy Interface to ODE Solvers</a><ul>
<li><a class="reference internal" href="#motivation">Motivation</a><ul>
<li><a class="reference internal" href="#traditional-approach">Traditional Approach</a><ul>
<li><a class="reference internal" href="#lsode">LSODE</a></li>
<li><a class="reference internal" href="#matlab">MATLAB</a></li>
<li><a class="reference internal" href="#python">Python</a></li>
</ul>
</li>
<li><a class="reference internal" href="#odespy-s-unified-interface">Odespy&#8217;s Unified Interface</a></li>
<li><a class="reference internal" href="#methods-and-implementations-offered-by-odespy">Methods and Implementations Offered by Odespy</a></li>
</ul>
</li>
<li><a class="reference internal" href="#installation">Installation</a></li>
<li><a class="reference internal" href="#basic-usage">Basic Usage</a><ul>
<li><a class="reference internal" href="#overview">Overview</a><ul>
<li><a class="reference internal" href="#step-1">Step 1</a></li>
<li><a class="reference internal" href="#step-2">Step 2</a></li>
<li><a class="reference internal" href="#step-3">Step 3</a></li>
<li><a class="reference internal" href="#step-4">Step 4</a></li>
<li><a class="reference internal" href="#step-5">Step 5</a></li>
<li><a class="reference internal" href="#step-6">Step 6</a></li>
</ul>
</li>
<li><a class="reference internal" href="#first-example-logistic-growth">First Example: Logistic Growth</a></li>
<li><a class="reference internal" href="#parameters-in-the-right-hand-side-function">Parameters in the Right-Hand Side Function</a></li>
<li><a class="reference internal" href="#continuing-a-previous-simulation">Continuing a Previous Simulation</a></li>
<li><a class="reference internal" href="#termination-criterion-for-the-simulation">Termination Criterion for the Simulation</a></li>
<li><a class="reference internal" href="#a-class-based-implementation">A Class-Based Implementation</a></li>
<li><a class="reference internal" href="#using-other-symbols">Using Other Symbols</a></li>
<li><a class="reference internal" href="#example-solving-an-ode-system">Example: Solving an ODE System</a></li>
<li><a class="reference internal" href="#the-euler-cromer-method">The Euler-Cromer Method</a></li>
<li><a class="reference internal" href="#testing-several-methods">Testing Several Methods</a></li>
</ul>
</li>
<li><a class="reference internal" href="#more-advanced-implementations">More Advanced Implementations</a><ul>
<li><a class="reference internal" href="#make-a-subclass-of-class-problem">Make a Subclass of Class Problem</a></li>
<li><a class="reference internal" href="#example-solving-a-complex-ode-problem">Example: Solving a Complex ODE Problem</a><ul>
<li><a class="reference internal" href="#quick-implementation">Quick Implementation</a></li>
<li><a class="reference internal" href="#comparison-of-methods">Comparison of Methods</a></li>
</ul>
</li>
<li><a class="reference internal" href="#avoiding-callbacks-to-python">Avoiding Callbacks to Python</a><ul>
<li><a class="reference internal" href="#the-logistic-ode">The Logistic ODE</a></li>
<li><a class="reference internal" href="#implementing-the-van-der-pol-equation-in-fortran">Implementing the van der Pol Equation in FORTRAN</a></li>
</ul>
</li>
<li><a class="reference internal" href="#example-solving-a-stochastic-differential-equation">Example: Solving a Stochastic Differential Equation</a></li>
</ul>
</li>
<li><a class="reference internal" href="#adaptive-methods">Adaptive Methods</a><ul>
<li><a class="reference internal" href="#the-test-problem">The Test Problem</a></li>
<li><a class="reference internal" href="#running-simple-methods">Running Simple Methods</a></li>
<li><a class="reference internal" href="#running-the-runge-kutta-fehlberg-method">Running the Runge-Kutta-Fehlberg Method</a></li>
<li><a class="reference internal" href="#testing-more-adaptive-solvers">Testing More Adaptive Solvers</a></li>
<li><a class="reference internal" href="#extensive-testing">Extensive Testing</a></li>
</ul>
</li>
<li><a class="reference internal" href="#solving-partial-differential-equations">Solving Partial Differential Equations</a><ul>
<li><a class="reference internal" href="#discretization-in-space">Discretization in Space</a></li>
<li><a class="reference internal" href="#implementation">Implementation</a><ul>
<li><a class="reference internal" href="#vectorized-code">Vectorized Code</a></li>
</ul>
</li>
<li><a class="reference internal" href="#experiments">Experiments</a></li>
</ul>
</li>
<li><a class="reference internal" href="#inner-workings-of-the-package">Inner Workings of the Package</a><ul>
<li><a class="reference internal" href="#solver-parameters">Solver Parameters</a></li>
<li><a class="reference internal" href="#solver-classes">Solver Classes</a><ul>
<li><a class="reference internal" href="#the-inherited-superclass-constructor">The Inherited Superclass Constructor</a></li>
<li><a class="reference internal" href="#useful-methods">Useful Methods</a></li>
<li><a class="reference internal" href="#the-solve-method">The Solve Method</a></li>
<li><a class="reference internal" href="#solver-attributes">Solver Attributes</a></li>
<li><a class="reference internal" href="#other-superclasses">Other Superclasses</a></li>
</ul>
</li>
<li><a class="reference internal" href="#a-very-simple-subclass">A Very Simple Subclass</a></li>
<li><a class="reference internal" href="#a-subclass-with-more-code">A Subclass with More Code</a></li>
<li><a class="reference internal" href="#a-simple-example-of-an-implicit-method">A Simple Example of an Implicit Method</a></li>
</ul>
</li>
<li><a class="reference internal" href="#troubleshooting">Troubleshooting</a><ul>
<li><a class="reference internal" href="#constructor-takes-exactly-two-arguments-5-given">Constructor takes exactly two arguments, 5 given</a></li>
</ul>
</li>
</ul>
</li>
</ul>

  <h4>Previous topic</h4>
  <p class="topless"><a href="index.html"
                        title="previous chapter">A Tutorial for the Odespy Interface to ODE Solvers</a></p>
  <h3>This Page</h3>
  <ul class="this-page-menu">
    <li><a href="_sources/main_odespy.txt"
           rel="nofollow">Show Source</a></li>
  </ul>
<div id="searchbox" style="display: none">
  <h3>Quick search</h3>
    <form class="search" action="search.html" method="get">
      <input type="text" name="q" size="18" />
      <input type="submit" value="Go" />
      <input type="hidden" name="check_keywords" value="yes" />
      <input type="hidden" name="area" value="default" />
    </form>
    <p class="searchtip" style="font-size: 90%">
    Enter search terms or a module, class or function name.
    </p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
        </div>
      </div>
      <div class="clearer"></div>
    </div>
    <div class="related">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             >index</a></li>
        <li class="right" >
          <a href="index.html" title="A Tutorial for the Odespy Interface to ODE Solvers"
             >previous</a> |</li>
        <li><a href="index.html">A Tutorial for the Odespy Interface to ODE Solvers</a> &raquo;</li> 
      </ul>
    </div>
<div class="wrapper">
  <div class="footer">
  <a href="http://cbc.simula.no"><img src="_static/cbc_banner.png" width="100%"><a>
  </div>
</div>

  </body>
</html>